虚拟继承
使用继承时,你可以指定 virtual
关键字:
struct A{};
struct B: public virtual A{};
当类 B
具有虚拟基础 A
时,意味着 A
将驻留在大多数派生类的继承树中,因此大多数派生类也负责初始化该虚拟基础:
struct A
{
int member;
A(int param)
{
member = param;
}
};
struct B: virtual A
{
B(): A(5){}
};
struct C: B
{
C(): /*A(88)*/ {}
};
void f()
{
C object; //error since C is not initializing it's indirect virtual base `A`
}
如果我们取消评论/*
A(88)*/
,我们将不会收到任何错误,因为 C
现在正在初始化它的间接虚拟基础 A
。
还要注意,当我们创建变量 object
时,大多数派生类都是 C
,所以 C
负责创建(调用构造函数)A
,因此 A::member
的值是 88
,而不是 5
(就像我们创建的对象一样) type B
)。
它在解决钻石问题时很有用。:
A A A
/ \ | |
B C B C
\ / \ /
D D
virtual inheritance normal inheritance
B
和 C
都继承自 A
,而 D
继承自 B
和 C
,因此在 D
中有 2 个 A
的实例! 当你通过 D
访问 A
的成员时,这会导致歧义,因为编译器无法知道你想从哪个类访问该成员(B
继承的那个,或者是 byC
继承的那个?)。
虚拟继承解决了这个问题:由于虚拟基础仅驻留在大多数派生对象中,因此 D
中只有一个 A
实例。
struct A
{
void foo() {}
};
struct B : public /*virtual*/ A {};
struct C : public /*virtual*/ A {};
struct D : public B, public C
{
void bar()
{
foo(); //Error, which foo? B::foo() or C::foo()? - Ambiguous
}
};
删除注释可以解决歧义问题。