42

考虑这个简单的层次结构:

class Base { public: virtual ~Base() { } };
class Derived : public Base { };

可以使用. Base* p_ 我曾经认为通过将 vtable 指针与对象中的指针进行比较来工作。Derived*dynamic_cast<Derived*>(p)dynamic_castpDerived

但是如果我们从中派生出另一个类Derived呢?我们现在有:

class Derived2 : public Derived { };

在这种情况下:

Base* base = new Derived2;
Derived* derived = dynamic_cast<Derived*>(base);

即使 in 中的 vtable 指针Derived2与 in 中的 vtable 指针无关,我们仍然可以成功地向下转型Derived

它实际上是如何工作的?如何dynamic_cast知道是否Derived2源自Derived(如果Derived在不同的库中声明会怎样)?

正在寻找有关其实际工作原理的具体细节(最好在 GCC 中,但其他也很好)。这个问题不是这个问题的重复(没有具体说明它是如何工作的)。

4

3 回答 3

30

如何dynamic_cast知道是否Derived2源自Derived(如果Derived在不同的库中声明会怎样)?

答案非常简单:dynamic_cast可以通过保留这些知识来了解这一点。

当编译器生成代码时,它会将有关类层次结构的数据保存在某种表中,dynamic_cast以便以后查找。该表可以附加到 vtable 指针,以便dynamic_cast实现轻松查找。这些类所需的数据typeid也可以与这些数据一起存储。

如果涉及到库,这种事情通常需要在库中公开这些类型信息结构,就像函数一样。例如,可能会得到一个看起来像“未定义的对 'vtable for XXX' 的引用”的链接器错误(天哪,这些很烦人!),就像函数一样。

于 2013-08-21T14:15:54.703 回答
18

魔法。

只是在开玩笑。如果你真的想详细研究这个,为 GCC 实现它的代码在 libsupc++ 中,它是 libstdc++ 的一部分。

https://github.com/mirrors/gcc/tree/master/libstdc%2B%2B-v3/libsupc%2B%2B

具体来说,查找名称中包含 tinfo 或 type_info 的所有文件。

或者阅读这里的描述,这可能更容易理解:

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti

这详细说明了编译器生成的类型信息的格式,并且应该为您提供运行时支持如何找到正确的转换路径的线索。

于 2013-08-21T14:15:07.150 回答
4

dynamic_cast 如何知道 Derived2 是否派生自 Derived(如果 Derived 在不同的库中声明会怎样)?

dynamic_cast本身什么都不知道,它的编译器知道这些事实。vtable 不一定只包含指向虚函数的指针。

这是我(天真地)这样做的方式:我的 vtable 将包含指向某些类型信息 (RTTI) 的指针dynamic_cast。一个类型的 RTTI 将包含指向基类的指针,所以我可以在类层次结构中向上。演员的伪代码如下所示:

Base* base = new Derived2; //base->vptr[RTTI_index] points to RTTI_of(Derived2)

//dynamic_cast<Derived*>(base):
RTTI* pRTTI = base->vptr[RTTI_index];
while (pRTTI && *pRTTI != RTTI_of(Derived))
{
  pRTTI = pRTTI->ParentRTTI;
}
if (pRTTI) return (Derived*)(base);
return NULL;
于 2013-08-21T14:28:24.643 回答