"virtual base class in the case of multilevel inheritance"有意义吗



考虑以下显示多级继承的示例代码:

案例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类的对象。

  1. derived2的对象中包含的子对象方面,案例1和案例2有何不同?

  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在这里的意思是"只包含一次"。

如果没有多重继承,则不能多次指定同一个类(循环继承是一个明显的语法错误)。然后是编译器中的优化有多聪明。如果编译器是完美的,那么没有区别。实际上,编译器可能会向类中添加与多重继承有关的内容。语言的某些特性使用的频率越低,编译器混淆的风险就越大。编译器通常是一个高质量的程序,但它仍然只是一个程序。

最新更新