【问题标题】:Does "virtual base class in the case of multilevel inheritance" have significance“多级继承情况下的虚拟基类”有没有意义
【发布时间】:2012-07-10 15:57:05
【问题描述】:

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

案例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. Case1 和Case2 在包含derived2 对象中的子对象方面有何不同?

  2. Case1 是否比 Case2 重要?

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

【问题讨论】:

  • +1 关于问题,因为我不得不投票赞成 Charles Bailey 的回答。

标签: c++ inheritance virtual virtual-inheritance


【解决方案1】:

使用虚拟继承时会存储额外的信息。这是为了在菱形情况下允许动态转换正确解析为派生类。 你的代码没有菱形的情况,所以没有用到信息。

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

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

【讨论】:

  • @user315052 :非常值得思考!!
  • 注意“隐式”基类的解释,它几乎是正确的,除非它不是:如果你在这里将虚拟继承设为私有,那么derived2 将不得不显式地base 继承,否则base 基址将无法在derived2 中访问。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      B: A
      C: A
      D: B, C
      

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

      【讨论】:

      • A 的继承可以是虚拟的,但非虚拟继承也可能是合适的。这是一个需要做出的设计决策。
      猜你喜欢
      • 2011-11-05
      • 1970-01-01
      • 2016-11-23
      • 2012-11-07
      • 2014-10-12
      • 2012-05-19
      • 2013-02-14
      • 1970-01-01
      • 2017-12-12
      相关资源
      最近更新 更多