钻石遗产的幕后发生了什么? 我在C++中使用菱形继承编写了一个代码,而不使用最高级类A的关键字“虚”,而定义继承B的类B和C。然后,我定义了继承B和C的类D,因为D类不使用类A成员X,编译不会抛出任何错误。但如果它这样做(通过取消对函数foo()的注释),编译器就会抛出错误,指出可以理解的歧义 #include<iostream> #include<cstdlib> using namespace std; class A { protected: int x; public: void set(int y){x=y;} }; class B:public A { }; class C:public A { }; class D:public B,public C { public: // void foo(){x*=2;} // error! }; int main() { cout<<sizeof(D)<<endl; return(0); }

钻石遗产的幕后发生了什么? 我在C++中使用菱形继承编写了一个代码,而不使用最高级类A的关键字“虚”,而定义继承B的类B和C。然后,我定义了继承B和C的类D,因为D类不使用类A成员X,编译不会抛出任何错误。但如果它这样做(通过取消对函数foo()的注释),编译器就会抛出错误,指出可以理解的歧义 #include<iostream> #include<cstdlib> using namespace std; class A { protected: int x; public: void set(int y){x=y;} }; class B:public A { }; class C:public A { }; class D:public B,public C { public: // void foo(){x*=2;} // error! }; int main() { cout<<sizeof(D)<<endl; return(0); },c++,inheritance,C++,Inheritance,现在foo()中使用的x没有任何歧义,因为它只有一个副本。因此,我的问题是: a) 在第一种情况下,编译器是否确实允许x的两个副本,因为它不必解决任何歧义 b) 如果(a)的答案是肯定的,那么为什么第一个代码为D类的大小输出8,第二个代码为相同的大小输出24?由于第一种情况下有两个x的副本,它的大小是否应该至少与第二种情况下的大小相同(即使我们忽略了填充)?a)是的。它实际上是重复的。明确地说,D包含成员B::x和C::x 如果将foo()修改为显式,则它可以工作。例如: void foo()

现在foo()中使用的x没有任何歧义,因为它只有一个副本。因此,我的问题是:

a) 在第一种情况下,编译器是否确实允许x的两个副本,因为它不必解决任何歧义

b) 如果(a)的答案是肯定的,那么为什么第一个代码为D类的大小输出8,第二个代码为相同的大小输出24?由于第一种情况下有两个x的副本,它的大小是否应该至少与第二种情况下的大小相同(即使我们忽略了填充)?

a)是的。它实际上是重复的。明确地说,D包含成员B::x和C::x

如果将foo()修改为显式,则它可以工作。例如:

void foo() { B::x *= 2; }
b) 在虚拟继承情况下占用额外内存的原因是需要虚拟表或vtable

你可以在这里找到一个很好的解释:

a)是的,你会有
B::x
C::x
,它们是不同的。2) 虚拟基地有额外的开销。
void foo() { B::x *= 2; }