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

我想知道“ 虚拟基类 ”是什么以及它意味着什么。 数组

让我举个例子: 函数

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

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

#1楼

关于内存布局

做为旁注,Dreaded Diamond的问题在于基类存在屡次。 所以,经过常规继承,您相信您拥有: 布局

A
 / \
B   C
 \ /
  D

可是在内存布局中,你有: this

A   A
|   |
B   C
 \ /
  D

这解释了为何在调用D::foo() ,你有一个歧义问题。 可是当你想要使用A的成员变量时, 真正的问题出现了。 例如,假设咱们有: spa

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 )。 code

这就是虚拟继承的便利之处,就像它同样,你将回到真正的钻石布局,不只有一个foo()方法,并且只有一个m_iValue对象

怎么可能出错?

想像: 继承

  • A有一些基本功能。
  • B增长了一些很酷的数据(例如)
  • C增长了一些很酷的功能,如观察者模式(例如,在m_iValue )。
  • D继承自BC ,所以来自A

使用正常继承,从D修改m_iValue是不明确的,必须解决此问题。 即便它是, D内有两个m_iValues ,因此你最好记住它并同时更新这两个。 接口

使用虚拟继承,从D修改m_iValue是能够的......可是......假设你有D 经过它的C接口,您附加了一个观察者。 经过它的B接口,你能够更新cool数组,它具备直接改变m_iValue ...... ip

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

结论

若是您的层次结构中有钻石,则表示您有95%的人对所述层次结构作错了。


#2楼

除了关于多重和虚拟继承的内容以外,还有一篇关于Dobb博士期刊的很是有趣的文章: 多重继承被认为是有用的


#3楼

这意味着对虚拟功能的调用将被转发到“正确”类。

C ++ FAQ Lite FTW。

简而言之,它一般用于多继承场景,其中造成“菱形”层次结构。 当您在该类中调用函数而且该函数须要被解析为该底层类之上的类D1或D2时,虚拟继承将打破底层中建立的歧义。 有关图表和详细信息,请参阅FAQ项目

它也用于姐妹表明团 ,这是一个强大的功能(虽然不适合胆小的人)。 请参阅常见问题

另见有效C ++第3版(第2版中的43)中的第40项。


#4楼

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

请考虑如下情形:

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 {};

这意味着层次结构中只包含一个A“实例”。 因而

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

但愿有助于做为迷你总结。 有关更多信息,请阅读本文此内容这里也有一个很好的例子。


#5楼

虚基类是没法实例化的类:您没法从中建立直接对象。

我认为你混淆了两件大相径庭的事情。 虚拟继承与抽象类不一样。 虚拟继承修改函数调用的行为; 有时它会解析函数调用,不然这些函数调用将是模糊的,有时它会将函数调用处理推迟到非虚拟继承中所指望的类。

相关文章
相关标签/搜索