考虑以下显示多级继承的示例代码:
案例1:这里derived1
类是通过虚拟继承从base
类派生的,derived2
类是直接从derived1
类派生的
class base
{
};
class derived1 : virtual public base
{
};
class derived2 : public derived1
{
};
案例2:与案例1相同,只是不涉及虚拟继承
class base
{
};
class derived1 : public base // no virtual inheritance
{
};
class derived2 : public derived1
{
};
假设我在这两种情况下都创建了一个derived2
类的对象。
在
derived2
的对象中包含的子对象方面,案例1和案例2有何不同?案例1比案例2有意义吗?
PS:我很清楚在多重继承过程中虚拟基类的重要性
如果继承层次结构中没有基类的多个实例,virtual
基类(至少)还有两个其他问题需要考虑。
首先,虚拟基类总是由在构造中且在非虚拟基类之前派生最多的类初始化。当中间类在其成员初始化列表中将参数传递给虚拟基类构造函数时,这一点最为明显。这些初始值设定项将被忽略。它还可以改变基类的构造顺序。
其次,不可能执行从虚拟基类到从其继承的类的static_cast
使用虚拟继承时存储了其他信息。这是为了在菱形情况下允许动态强制转换正确地解析为派生类
如果您向基类添加数据成员,则可以使附加信息可见:
class base {
protected:
int i;
};
如果现在在两个案例中的每一个案例中打印每个派生类的大小,您将观察到它们的大小在不同案例中的差异。
编辑:Charles Bailey在使用虚拟继承的语义差异方面提出了很好的观点。他的第一点特别有意思,应该给予更多的考虑。他的观点基本上是derived2
具有隐含的菱形拓扑。也就是说,假设derived1
实际上继承自base
:
class base {};
class derived1 : virtual public base {};
那么,这三个版本的derived2
没有区别,它们的行为都像第一个版本:
class derived2 : virtual public base, public derived1 {};
class derived2 : public derived1, virtual public base {};
class derived2 : public derived1 {}
这意味着当您从类似derived1
的类(即使用了虚拟继承的类)派生时,会创建一个隐式菱形。
base
|
| .(virtual)
| /
| /___
| |
| derived1
| /
| /
|./
/
/___
|
derived2
为了进一步说明这一点,当derived1
使用虚拟继承时,这是允许的:
class derived2 : public derived1 {
public:
derived2 () : base() {}
};
但是,如果derived1
不使用虚拟继承,则是不允许的。
虚拟继承在您的案例中没有任何意义
它只为钻石继承而缩进,如:
B: A
C: A
D: B, C
在这种情况下,在这两种情况下,A的继承都应该是虚拟的。
只有当类作为基类多次包含时,虚拟继承才会变得重要。事实上,说明符virtual
在这里的意思是"只包含一次"。
如果没有多重继承,则不能多次指定同一个类(循环继承是一个明显的语法错误)。然后是编译器中的优化有多聪明。如果编译器是完美的,那么没有区别。实际上,编译器可能会向类中添加与多重继承有关的内容。语言的某些特性使用的频率越低,编译器混淆的风险就越大。编译器通常是一个高质量的程序,但它仍然只是一个程序。