虚拟继承混淆

Yuk*_*uki 5 c++ inheritance virtual-inheritance c++11

我正在阅读关于继承的内容,我有一个我几个小时都无法解决的重大问题:

鉴于一个类Bar是一个具有virtual函数的类,

class Bar
{
    virtual void Cook();
};
Run Code Online (Sandbox Code Playgroud)

有什么不同:

class Foo : public Bar
{
    virtual void Cook();
};
Run Code Online (Sandbox Code Playgroud)

class Foo : public virtual Bar
{
    virtual void Cook();
};
Run Code Online (Sandbox Code Playgroud)

?谷歌搜索和阅读的时间提供了大量有关其用途的信息,但实际上没有人告诉我两者之间有什么区别,只是让我更加困惑.

iam*_*ind 5

功能明智,两个版本之间没有太大区别.对于virtual继承的情况,每个实现通常都会添加一个(vptr类似)指针(与virtual函数的情况相同).这有助于避免由于多重继承而生成多个基类副本(钻石继承问题)

此外,virtual继承委托调用其基类的构造函数的权限.例如,

class Bar;
class Foo : public virtual Bar
class Other : public Foo  // <--- one more level child class
Run Code Online (Sandbox Code Playgroud)

所以,现在Bar::Bar()将直接调用Other::Other(),也将放在其他基类中的第一位.

委派功能有助于final class在C++ 03 中实现(在Java中)功能:

class Final {
  Final() {}
  friend class LastClass;
};

class LastClass : virtual Final {  // <--- 'LastClass' is not derivable
...
};

class Child : public LastClass { // <--- not possible to have object of 'Child'
};
Run Code Online (Sandbox Code Playgroud)


Jam*_*nze 4

仅当类要继承自 时,虚拟继承才相关 Foo。如果我定义以下内容:

class B {};
class L : virtual public B {};
class R : virtual public B {};
class D : public L, public R {};
Run Code Online (Sandbox Code Playgroud)

那么最终的对象将只包含 的一份副本B,由 L和共享R。如果没有virtual,类型的对象D将包含 的两份副本B,一份 in L,一份 in R

有一些争论认为所有继承都应该是虚拟的(因为在它产生影响的情况下,这正是您大多数时候想要的)。然而,实际上,虚拟继承是昂贵的,并且在大多数情况下是不必要的:在一个设计良好的系统中,大多数继承只是从一个或多个“接口”继承的具体类;这样的具体类通常不会被设计为从自身派生,因此没有问题。但也有一些重要的例外:例如,如果您定义一个接口,然后对该接口进行扩展,则扩展应该从基本接口虚拟继承,因为具体实现可能需要实现多个扩展。或者,如果您正在设计 mixins,其中某些类仅实现接口的一部分,而最终类继承自其中的几个类(接口的每个部分一个)。最后,是否虚拟继承的标准并不太难:

  • 如果继承不是公共的,它可能不应该是虚拟的(我从未见过例外),否则

  • 如果该类没有设计为基类,则不需要虚拟继承,否则

  • 继承应该是虚拟的。

有一些例外,但上述规则在安全方面是错误的;即使在不需要虚拟继承的情况下,虚拟继承通常也是“正确的”。

最后一点:虚拟基必须始终由最派生的类初始化,而不是直接继承的类(并声明继承是虚拟的)。然而,在实践中,这不是问题。如果您查看虚拟继承有意义的情况,您会发现它始终是从接口继承的情况,该接口不包含任何数据,因此(仅)有一个默认构造函数。如果您发现自己实际上从具有带参数的构造函数的类继承,那么是时候提出一些有关设计的严肃问题了。