类中“虚拟”的含义

时间:2012-01-01 15:15:23

标签: c++ virtual-inheritance

在下面的例子中,virtual用于解决钻石问题,使得A类的子对象与B和C共享。

例如:

class A { };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };

这种类型的继承是在编译时还是在运行时解决的?我的意思是,虚函数在函数和类上使用时的含义有何不同?当虚拟用于从基类继承时,是否存在动态绑定的概念?

4 个答案:

答案 0 :(得分:1)

虚拟继承在运行时解析。

虚拟继承,就像虚函数一样,意味着类的每个实例都可以访问描述可以找到虚拟事物的位置的运行时数据。

它在How C++ virtual inheritance is implemented in compilers?

处描述失败

答案 1 :(得分:1)

继承不必像你所说的那样“解决”,所以问题不是很清楚。

重要的区别在于普通继承和虚拟继承。如果您通常继承,即B : AC : A,则类D : B, C具有两个类型为A子类,即D::B::AD::C::A。另一方面,如果BCA继承虚拟,那么最终的子类组合将被推迟,直到您定义最终类型。也就是说,B : virtual AC : virtual A本身都有一个虚拟子类A,如果您要实例化BC,它们将成为现实。另一方面,如果您从类中进一步派生,那么派生程度最高的类将只包含一个<{1}}类型的子类。

也许您可能会考虑使用成员函数进行类比。如果每个派生类添加一个与基本函数同名的成员函数,则最终会得到几个不同的函数。另一方面,如果基函数是A,那么您只有一个函数,该函数在最派生类中定义。在每个中间类中仍然有一些类函数(假设函数不是纯虚函数),但只有最终类定义了“活动”定义。

虚拟继承对构造函数有影响,即虚拟基础构造函数(即示例中的virtual)由大多数派生的类直接调用,即{{1} } {和 A()D。如果你愿意,这是因为只有B“知道”它只包含一个C - 子类,所以它直接“负责”它。 DA只需拥有一个虚拟占位符,让位于最终,最派生的类。

通过任何基指针/引用访问成员函数的行为与预期一致,并以通常的方式解析(即动态地,通常),但这与虚拟继承无关。实际的函数查找可能有点复杂,因为它可能涉及额外的间接级别,但这不会改变任何基本的。

答案 2 :(得分:1)

  

这种类型的继承是在编译时还是在运行时解决?

继承定义类型的形状(内存占用),并在编译时解析始终。另一方面,对基本类型成员的访问将在运行时解析,原因是层次结构中的中间类型不能知道基本子对象将在内存中放置在最多派生的位置类型。

  

我的意思是,虚函数在函数和类上使用时的含义有何不同?

各个级别的类型和功能都不同,所以这里没什么好说的。唯一常见的是,有一部分工作在编译时无法完全解决。特别是,相似之处在于使用虚函数的代码依赖于vptr(虚拟表指针)来为最派生类型找到正确的vtable(虚拟表),并且以类似的方式,对基础对象的访问需要使用存储在几乎从基础派生的类型的每个子对象中的指针。

您可以在Itanium C++ ABI中阅读更多(关于特定实施,所有这些不是标准的一部分),特别是Non-POD Class Types

作为简要总结,每当类型D从类型B虚拟地继承时,D子对象将包含指向B子对象的隐藏指针。对指针的需求源于这样一个事实,即B子对象相对于D的相对位置可以随着进一步的继承而改变。

答案 3 :(得分:0)

据我所知,虚拟类解决了编译时的歧义。例如,假设B和C都有getSize方法。

如果没有虚拟,则调用D.getSize会尝试使用基类getSize方法。由于B和C都有getSize方法,编译器无法正确解决歧义,代码也无法编译。

使用virtual,对D.getSize的调用使用decedent getSize方法,允许代码正确编译。