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

c++ virtual-inheritance

295208 观看

10回复

5700 作者的声誉

我想知道什么是“ 虚拟基类 ”及其含义。

让我举一个例子:

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

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};
作者: popopome 的来源 发布者: 2008 年 8 月 22 日

回应 10


1

45402 作者的声誉

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

C ++ 常见问题精简版 FTW。

简而言之,它经常用于形成“钻石”层次结构的多继承方案中。当您在该类中调用函数时,虚拟继承将打破在底层类中创建的歧义,并且需要将该函数解析为该底层类之上的D1或D2类。有关图表和详细信息,请参见FAQ项目

它在姐妹代表团中也使用,这是一个强大的功能(尽管不是胆小的人)。请参阅常见问题解答。

另请参见有效C ++第三版(第二版43)中的第40项。

作者: wilhelmtell 发布者: 2008 年 8 月 22 日

520

25749 作者的声誉

决定

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

请考虑以下情形:

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() ??

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

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

希望能对您有所帮助。有关更多信息,请阅读thisthis这里也有一个很好的例子。

作者: OJ. 发布者: 2008 年 8 月 22 日

9

45402 作者的声誉

虚拟基类是无法实例化的类:您无法从中创建直接对象。

我认为您混淆了两个截然不同的事物。虚拟继承与抽象类不是一回事。虚拟继承修改了函数调用的行为。有时,它解决了否则可能会造成歧义的函数调用;有时,它将函数调用处理推迟到了非虚拟继承所期望的类之外的其他类上。

作者: wilhelmtell 发布者: 2008 年 8 月 22 日

1

4330 作者的声誉

你有点困惑。我不知道您是否混淆了一些概念。

您的OP中没有虚拟基类。您只有一个基类。

您进行了虚拟继承。通常在多重继承中使用它,以便多个派生类使用基类的成员而不重现它们。

具有纯虚函数的基类不会被实例化。这需要Paul掌握的语法。通常使用它,以便派生类必须定义那些函数。

我不想再对此进行解释,因为我无法完全理解您的要求。

作者: Baltimark 发布者: 2008 年 8 月 22 日

0

7850 作者的声誉

虚拟类是一样的虚拟继承。您无法实例化的虚拟类,虚拟继承完全是另外一回事。

维基百科比我能更好地描述它。http://en.wikipedia.org/wiki/Virtual_inheritance

作者: bradtgmurray 发布者: 2008 年 8 月 22 日

6

45402 作者的声誉

我想补充一下OJ的种类说明。

虚拟继承并非没有代价。就像虚拟的所有事物一样,您会受到性能的影响。解决这种性能问题的方法可能不太优雅。

您可以通过在钻石上添加另一层来获得类似于以下内容的内容,而不是通过虚拟衍生来破坏钻石:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

没有一个类是虚拟继承的,所有类都是公开继承的。然后,类D21和D22将隐藏对于DD不明确的虚函数f(),也许是通过将函数声明为私有的。他们分别定义了一个包装函数f1()和f2(),每个函数都调用类本地(私有)f(),从而解决了冲突。类DD如果需要D11 :: f()则调用f1(),如果需要D12 :: f()则调用f2()。如果您内联定义包装器,则可能会获得零开销。

当然,如果可以更改D11和D12,则可以在这些类中执行相同的操作,但是通常不是这种情况。

作者: wilhelmtell 发布者: 2008 年 8 月 22 日

240

66692 作者的声誉

关于内存布局

附带说明一下,Dreaded Diamond的问题在于基类存在多次。因此,通过常规继承,您相信自己具有:

  A
 / \
B   C
 \ /
  D

但是在内存布局中,您有:

A   A
|   |
B   C
 \ /
  D

这解释了为什么在通话时D::foo(),您有歧义性问题。但是,当您要使用的成员变量时,就会出现真正的问题A。例如,假设我们有:

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

当您尝试从访问m_iValueD,编译器会提出抗议,因为在层次结构中,它将看到两个m_iValue,而不是一个。而且,如果您修改一个,例如B::m_iValue(是的A::m_iValue父项B),C::m_iValue则不会被修改(这是的A::m_iValue父项C)。

在这里,虚拟继承很方便,与之类似,您将回到真正的菱形布局,不仅使用一种foo()方法,而且还使用一种方法,并且只有一种方法m_iValue

可能出什么问题了?

想像:

  • A 具有一些基本功能。
  • B 向其中添加一些很酷的数据数组(例如)
  • C向其中添加了一些很酷的功能,例如观察者模式(例如on m_iValue)。
  • D从继承BC,并且因此从A

对于正常的继承,m_iValueD进行修改是不明确的,必须解决。即使是这样,有两个m_iValues里面D,所以你最好记住,并在同一时间更新这两个。

使用虚拟继承,可以m_iValueD进行修改...但是...假设您有D。通过其C界面,您连接了一个观察者。通过其B界面,您可以更新很酷的数组,它的副作用是直接更改m_iValue...

由于更改m_iValue是直接完成的(不使用虚拟访问器方法),因此C不会调用观察者“监听” ,因为实现监听的代码在中C,并且B对此一无所知。

结论

如果您的层次结构中有钻石,则意味着您有95%的人对该层次结构做错了事。

作者: paercebal 发布者: 2008 年 9 月 21 日

4

26760 作者的声誉

除了已经说过的关于多重继承和虚拟继承的内容外,Dobb博士的期刊上有一篇非常有趣的文章:多重继承被认为是有用的

作者: Luc Hermitte 发布者: 2008 年 9 月 22 日

32

1022 作者的声誉

用虚拟基础解释多重继承需要了解C ++对象模型。最好在文章中而不是在评论框中清楚地说明主题。

我发现可以解决我对此问题的所有疑问的最好的可读性解释是这篇文章:http : //www.phpcompiler.org/articles/virtualinheritance.html

阅读完该主题之后,您真的不需要阅读任何其他有关该主题的内容(除非您是编译器作者)...

作者: lenkite 发布者: 2009 年 6 月 13 日

1

176946 作者的声誉

Diamond继承可运行用法示例

本示例说明如何在典型方案中使用虚拟基类:解决菱形继承。

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}
作者: Ciro Santilli 新疆改造中心法轮功六四事件 发布者: 2016 年 12 月 7 日
32x32