在C ++中,什么是虚拟基类?

我想知道“虚拟基类”是什么以及它的意思。

让我举个例子:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};

在虚拟继承中使用的虚拟基类是在使用多重继承时防止给定类的多个“实例”出现在继承层次结构中的一种方法。

考虑以下情况:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

上面的类层次结果导致“可怕的钻石”,看起来像这样:

  A
 / 
B   C
  /
  D

D的一个实例将由B组成,其中包括A和C,其中也包括A.因此,您有两个“实例”(为了更好的表达式)A

当你有这种情况时,你有可能模棱两可。 当你这样做时会发生什么:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

虚拟继承可以解决这个问题。 当你继承你的类时指定虚拟的,你告诉编译器你只需要一个实例。

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

这意味着层次结构中只包含一个“实例”。 于是

D d;
d.Foo(); // no longer ambiguous

希望有助于作为一个小小的总结。 欲了解更多信息,请阅读本文和本文。 这里也有一个很好的例子。


关于内存布局

作为一个方面说明,Dreaded Diamond的问题在于基类存在多次。 所以通过定期的继承,你相信你有:

  A
 / 
B   C
  /
  D

但在内存布局中,您有:

A   A
|   |
B   C
  /
  D

这解释了为什么当调用D::foo() ,你有一个模糊问题。 但是当你想使用A的成员变量时, 真正的问题就出现了。 例如,假设我们有:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

当你试图从D访问m_iValue时,编译器会抗议,因为在层次结构中,它会看到两个m_iValue ,而不是一个。 如果你修改一个,比方说, B::m_iValue (也就是A::m_iValue的父B ), C::m_iValue不会被修改(也就是A::m_iValue的母公司C )。

这就是虚拟继承派上用场的地方,就像它一样,你会回到真正的菱形布局,不仅有一个foo()方法,而且还有一个并且只有一个m_iValue

有什么可能出错?

想像:

  • A有一些基本功能。
  • B增加了一些很酷的数据阵列(例如)
  • C增加了一些很酷的功能,如观察者模式(例如,在m_iValue )。
  • DBC继承,因此从A继承。
  • 在正常继承的情况下,从D修改m_iValue是不明确的,必须解决。 即使是这样, D里面也有两个m_iValues ,所以你最好记住它并且同时更新它们。

    使用虚拟继承,从D修改m_iValue是可以的......但是...假设您有D 通过它的C接口,你附加了一个观察者。 并通过它的B接口,更新酷阵,具有直接更改m_iValue ...

    由于m_iValue的更改是直接完成的(不使用虚拟访问器方法),所以通过C “监听”的观察者将不会被调用,因为实现监听的代码是C ,而B不知道它。 。

    结论

    如果你的层次结构中有钻石,这意味着你有95%的人在所述层次结构中做了错误的事情。


    用虚拟基础解释多重继承需要了解C ++对象模型。 并且明确地解释这个话题最好是在文章中而不是在评论框中完成。

    我发现最好的,可读的解释解决了关于这个问题的所有疑问,这篇文章是:http://www.phpcompiler.org/articles/virtualinheritance.html

    阅读后,你真的不需要阅读关于该主题的任何其他内容(除非你是编译器作家)......

    链接地址: http://www.djcxy.com/p/78935.html

    上一篇: In C++, what is a virtual base class?

    下一篇: thread is determined during compile or runtime?