1

我知道虚拟继承的用法:

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

我想知道的是

class X { public: void Bar() {} };
class Y : public virtual X {};

class X { public: void Bar() {} };
class Y : public X {};

主要是从执行的角度。我的意思是我什么时候不想使用虚拟继承?

4

4 回答 4

1

首先,虚拟继承会将所有相同类型的虚拟基“合并”成一个基子对象,由完整对象中的所有派生类型共享。你希望这种情况发生吗?如果您希望此类基础由单独的基础子对象表示,则不能使用虚拟继承。因此,您的意图很明显。

在您的第一个示例中,相同的单个A子对象将由 classes 共享BC并且D在 type 的完整对象中D。如果继承是非虚拟的,那么B就会有它自己的独立的A并且C会有它自己的独立的A. 这显然会影响任何依赖As 对象标识的代码的行为。

所以,从这个角度来看,这对你来说确实是一个问题:你想实现什么?单个共享基础子对象?还是多个独立的基础子对象?

(您的X示例Y不具有代表性。虚拟继承的上述属性仅在多继承配置中显示出来。在单继承情况下,虚拟继承除了施加潜在的性能/空间开销之外什么也没有。)

其次,为了提供从所有派生类到单个共享基类子对象的访问(在虚拟继承的情况下),编译器将使用运行时实现结构。通常派生类将通过嵌入到每个具有虚拟基的对象中的指针来访问它们的虚拟基。这会带来性能损失,并且需要在对象布局中增加额外空间。

同时,“正常”(非虚拟)继承会产生固定的编译时布局,这意味着类之间的所有转换都是通过添加或减去编译器立即知道的固定偏移量来执行的。这更快,并且不需要额外的运行时信息。

于 2013-06-15T15:57:52.190 回答
1

不同之处在于将生成虚拟继承类的Vbtable您可以在这篇文章中了解这一点:

为多重虚拟继承生成一个vbtable(虚拟基类表)。因为有时需要升级(转换为基类),所以需要确定基类的确切位置。vbtable 包含每个基类的 vftable 的位移,它实际上是派生类中基类的开始。

于 2013-06-15T16:21:56.657 回答
0

其他答案很好,所以先阅读它们。

现在,让我添加虚拟继承:

class X { public: void Bar() {} };
class Y : public virtual X {};

您将无法从X对封闭的引用中向下转换Y

Y y;
X &rx = y; // refers to the X base class subobject
Y &ry = ??? (rx); // how to get back a reference to y from rx

无法获得对Yfrom的引用rx

  • 要么,static_cast<Y&> (x)因为它只适用于非虚拟继承
  • dynamic_cast<Y&> (x)只有在X具有某些虚拟功能时才有效

但是你所要做的就是添加一些虚函数:

class X {
public: 
    void Bar() {} 
    virtual ~X () {}
};

为要派生的类设置一个虚拟析构函数是个好主意。此一般规则很少有例外(主要是具有受保护析构函数的类)。

于 2013-06-16T07:11:32.057 回答
-1

虚拟继承的用途是处理多重继承,如下图

在此处输入图像描述

在第二种情况下,从 A 调用(虚拟)方法或函数的子类 D 不知道它是通过 B 还是通过 C 调用它。通过虚拟继承,两个“路径被一起获取”。

于 2013-06-15T15:53:54.857 回答