4

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

Case1:这里类derived1base通过虚拟继承从类derived2派生而来的,类是derived1直接从类派生而来的。

class base
{

};

class derived1 : virtual public base
{

};

class derived2 : public derived1
{

};

Case2 :与 Case1 相同,只是不涉及虚拟继承

class base
{

};

class derived1 : public base // no virtual inheritance
{

};

class derived2 : public derived1
{

};

derived2假设我在这两种情况下都创建了一个类的对象。

  1. Case1 和 Case2 在子对象的包含方面有何不同derived2

  2. Case1 是否对 Case2 有意义?

PS:我很清楚虚拟基类在多重继承期间的重要性。

4

4 回答 4

3

如果在继承层次结构中没有基类的多个实例,则(至少)还有两个其他问题需要考虑virtual基类。

首先,虚拟基类总是由正在构造的最派生类在非虚拟基类之前初始化。当中间类在其成员初始化列表中将参数传递给虚拟基类构造函数时,这一点最为明显。这些初始化程序将被忽略。它还可以对基类的构造顺序产生影响。

其次,不可能执行static_cast从虚拟基类到继承自它的类的操作。

于 2012-07-11T05:54:53.280 回答
2

使用虚拟继承时会存储其他信息。这是为了在菱形情况下允许动态转换正确解析为派生类。您的代码没有菱形情况,因此不使用该信息。

如果将数据成员添加到基类,则可以使附加信息可见:

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不使用虚拟继承是不允许的。

于 2012-07-11T05:15:25.123 回答
1

虚拟继承在您的情况下没有意义。
它仅对 diamon 继承缩进,例如:

B: A
C: A
D: B, C

在这种情况下,两种情况下从 A 的继承都应该是虚拟的。

于 2012-07-11T04:59:19.200 回答
1

仅当该类多次作为基类包含时,虚拟继承才变得重要。实际上,说明符virtual在这里的意思是“只包含一次”。

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

于 2012-07-11T05:09:39.613 回答