使用虚拟基类在C ++中向下转换

时间:2019-01-01 07:58:24

标签: c++

考虑场景:

class Base { };

class Derived: public virtual Base {};

int main(void) {
    Derived * d = new Derived();

    Base * b = d;

    Derived * x = static_cast<Derived*>(b); // Probably impossible

    delete x;
}

在这里,从指向Base的指针到指向Derived的指针的向下转换给出了一个编译错误,提示“由于base是虚拟的,因此无法转换”。由于Base不是多态类型,因此我无法在此处动态转换。我的问题是:

  1. 这根本不可能吗?
  2. C ++标准(11或14)如何声明这是不可能的?

如果不可能,我想了解原因。我知道虚拟方法是通过vptr表实现的,但是如何实现虚拟基类呢?我也知道C ++标准没有定义实现,但是我还是想了解gcc之类的常见实现。所以问题是:

  1. 像gcc这样的标准编译器的底层实现是什么,使得该功能无法实现?

对于那里的所有C ++专家来说,这可能是一个相当简单的问题,但是经过半个小时的搜索,我只是找不到任何具体的东西。如果已经涵盖,请随时进行复制,下票等。

1 个答案:

答案 0 :(得分:5)

  

这根本不可能吗?

是的

  

C ++标准(11或14)如何声明这是不可能的?

根据static_cast的要求进行说明。

  

[expr.static.cast] -强调我的

     

11类型为“指向cv1 B的指针”的prvalue,其中B是类类型,   可以转换为“ pointer to cv2 D”类型的prvalue,其中D是   如果有效的标准,则从B派生的类(Clause [class.derived])   存在从“指针到D”到“指针到B”的转换([conv.ptr]),   cv2与cv具有相同的cv资格,或具有比cv更高的cv资格,   cv1,和B既不是D的虚拟基类也不是D的基类   D的虚拟基类。

如您所见,Base不能是虚拟的,甚至不能是另一个虚拟基础的非虚拟基础。

  

像gcc这样的标准编译器的底层实现是什么,使得该功能无法实现?

虚拟继承是一种允许在不同的中间基类之间共享同一Base子对象的机制。例如,如果要添加:

class Derived2: public virtual Base {};
class MostDerived: public Derived, public Derived2 {};

然后MostDerived将具有类型Derived和类型Derived2的子对象,但是与常规继承不同,它们不会各自具有自己的Base子对象,对象(因此Base中有2个MostDerived子对象)。相反,将只有一个Base子对象,并且DerivedDerived2都将引用它。

通常通过DerivedDerived2通过某个指针间接访问Base来实现。他们不能依靠它以一定的偏移量放置在自己内部。它们的Base的位置由最派生的对象确定。因此,如果您拥有Base*,则没有确定的方法可以访问引用它的Derived对象。

在非虚拟继承的情况下,Base将位于编译器相对于完全包含Derived子对象的位置。因此,编译器可以执行获取Derived子对象所需的指针算法。但您的情况却不能,因为不能保证相对位置。该访问(通常)是间接的。


您没有问,但我也可以解决。 dynamic_cast适用于多态类的原因是,多态类可以在其vtable中具有与其关联的RTTI(如果它们具有一个)。而且由于Base*将指向MostDerived(或Derived)的vtable,所以dynamic_cast机制可以利用该表中写入的信息来弄清楚如何获取子表。 -它需要的对象。该表可以包含从任何一个子对象到任何其他子对象所需的所有偏移信息。而且由于它是(在运行时)派生程度最高的对象的表,因此该信息是固定且可靠的。