【问题标题】:Virtual Inheritance Confusion虚拟继承混淆
【发布时间】:2011-12-30 05:06:45
【问题描述】:

我正在阅读有关继承的内容,但我遇到了一个几个小时都无法解决的主要问题:

给定一个类Bar 是一个具有virtual 函数的类,

class Bar
{
    virtual void Cook();
};

两者有什么区别:

class Foo : public Bar
{
    virtual void Cook();
};

class Foo : public virtual Bar
{
    virtual void Cook();
};

?几个小时的谷歌搜索和阅读得到了很多关于它的用途的信息,但没有人真正告诉我两者之间的区别是什么,这让我更加困惑。

【问题讨论】:

标签: c++ inheritance c++11 virtual-inheritance


【解决方案1】:

虚函数是一个在派生类中可能有不同实现的函数(虽然不是必须的)。

在您的最后一个示例中是虚拟继承。想象一下,您有两个类(A 和 B)派生自一个基类(我们称其为“Base”)。现在想象从 A 和 B 派生的第三个类 C。如果没有虚拟继承,C 将包含两个“Base”副本。这可能会导致编译时出现歧义。虚拟继承中的重要一点是,必须在类 C 中提供“Base”类构造函数(如果有)的参数,因为来自 A 和 B 的此类调用将被忽略。

【讨论】:

  • 编译时可能会导致歧义。”这导致歧义iff你尝试@987654321 @IS-A Base.
【解决方案2】:

只有类要继承时,虚拟继承才有意义 Foo。如果我定义以下内容:

class B {};
class L : virtual public B {};
class R : virtual public B {};
class D : public L, public R {};

那么最终对象将只包含B 的一份副本,由双方共享 LR。如果没有virtualD 类型的对象将包含 两份B,一份在L,一份在R

有一些观点认为所有继承都应该是虚拟的(因为 在它有所作为的情况下,这就是你最想要的 时间)。然而,在实践中,虚拟继承是昂贵的,而且 在大多数情况下,没有必要:在一个设计良好的系统中,大多数 继承将只是一个具体类继承自一个或 更多“接口”;这样一个具体的类通常不是设计成 来源于自身,所以没有问题。但有重要的 例外:例如,如果您定义了一个接口,然后 接口的扩展,扩展应该虚拟继承 从基本接口,因为一个具体的实现可能想要 实现几个扩展。或者如果你正在设计 mixins,在哪里 某些类只实现了部分接口,而最终 类继承自其中几个类(每个部分一个 界面)。最后,是否虚拟继承的标准 与否并不难:

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

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

  • 继承应该是虚拟的。

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

最后一点:虚拟基础必须始终由最 派生类,不是直接继承的类(并声明 继承是虚拟的)。然而,在实践中,这不是问题。 如果您查看虚拟继承有意义的情况,那就是 总是从接口继承的情况,其中不包含 数据,因此(仅)具有默认构造函数。如果你发现自己 从具有构造函数的类中虚拟继承 争论,是时候就设计提出一些严肃的问题了。

【讨论】:

    【解决方案3】:

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

    另外,virtual 继承授权调用其基类的构造函数。例如,

    class Bar;
    class Foo : public virtual Bar
    class Other : public Foo  // <--- one more level child class
    

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

    委托功能有助于在 C++03 中实现 final class(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'
    };
    

    【讨论】:

      【解决方案4】:

      在这种情况下,没有区别。虚拟继承与派生类共享超类子对象实例有关

      struct A
      {
        int a;
      };
      
      struct B : public virtual A
      {
        int b;
      }
      
      struct C : public virtual A
      {
        int c;
      };
      
      struct D : public B, public C
      {
      };
      

      D 的实例中有成员变量a 的单个副本;如果A 不是虚拟基类,则D 的实例中将有两个A 子对象。

      【讨论】:

      • "在这种情况下,没有区别。" 直到您尝试使用 static_cast!
      猜你喜欢
      • 2012-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      • 2013-08-24
      相关资源
      最近更新 更多