这仍然可以解决钻石问题吗?
class A
{};
class B : virtual A
{};
class C : A
{};
class D : B, C
{};
编辑:如果没有,那么它是什么?它和它一样吗?
class A
{};
class B : A
{};
class C : A
{};
class D : B, C
{};
或者甚至是其他什么东西?
答案 0 :(得分:2)
钻石问题和对共同基础成员的模糊调用可以通过以下图示等效来最好地描述,这也可以提供对内存模型的深入了解
class A
{void foo(){};};
class B :public A
{};
class C :public A
{};
class D :public B,public C
{};
由于继承只是一个接一个地放置两个对象的实现,所以基类A的所有方法都在派生类B和C中重复,它具有等效的图形表示
A A
| |
| |
B C
\ /
\ /
D
class A
{};
class B :public virtual A
{};
class C :public A
{};
class D :public B,public C
{};
使用virtual关键字,编译器在B的vtable中为Derived类B生成vtable,其vptr(虚拟指针)存储Base类A的Offset。对于C,它仍然复制基类A的所有方法所以D类,通过Base类B通过vptr引用基类A的成员,其中C类引用基类A的成员作为副本。因此,钻石问题和模糊性仍然存在。
A A
/ |
/ |
B C
\ /
\ /
D
class A
{};
class B :public virtual A
{};
class C :public virtual A
{};
class D :public B,public C
{};
现在当派生类B和C都虚拟地继承A时,编译器为B和C创建一个Vtable,并在每个vtable中创建一个vptr来引用基类A的偏移量。这最终解决了钻石问题因为通过B或C只能看到一个副本
A
/ \
/ \
B C
\ /
\ /
D
答案 1 :(得分:0)
不,它不会。 B
和C
都必须虚拟地从A
继承。
答案 2 :(得分:0)
如果从A类到B的继承标记为虚拟但不是A类到C,那么C ++将创建单个虚拟A(D继承B,B继承A)和非虚拟A(D继承C,C继承A )。因此,如果要访问A的成员,以下代码将无法解决菱形问题。
class A
{};
class B : virtual A
{};
class C : A
{};
class D : B, C
{};
它和它一样吗?
class A
{};
class B : A
{};
class C : A
{};
class D : B, C
{};
上面的代码将为A的每个继承路径创建两个非虚拟A(D继承B,B继承A; D继承C,C继承A)。
因此,两种情况的结果是相同的:钻石问题无法解决。
最后,你应该为从A到B和A到C的继承做出“虚拟”。在这种情况下,C ++只会创建一个A对象。