考虑这个简单的层次结构:
class Base { public: virtual ~Base() { } };
class Derived : public Base { };
使用Base* p
可以尝试将Derived*
转发给dynamic_cast<Derived*>(p)
。我曾经认为dynamic_cast
的作用是将p
中的vtable指针与Derived
对象中的vtable指针进行比较。
但是如果我们从Derived
推导出另一个类呢?我们现在有:
class Derived2 : public Derived { };
在这种情况下:
Base* base = new Derived2;
Derived* derived = dynamic_cast<Derived*>(base);
即使Derived2
中的vtable指针与Derived
中的vtable指针无关,我们仍然可以获得成功的向下转换。
它是如何实际运作的? dynamic_cast
如何知道Derived2
是否来自Derived
(如果Derived
在另一个库中声明了怎么办?)
我我正在寻找有关其实际工作方式的具体细节(最好是在海湾合作委员会,但其他人也很好)。此问题不是this question的副本(未指定其实际工作方式)。
答案 0 :(得分:25)
dynamic_cast
如何知道Derived2
是否来自Derived
(如果Derived
在另一个库中声明了怎么办?)
答案非常简单:dynamic_cast
可以通过掌握这些知识来了解这一点。
当编译器生成代码时,它会保留dynamic_cast
稍后可以查找的某种表格中有关类层次结构的数据。该表可以附加到vtable指针,以便dynamic_cast
实现轻松查找。这些类的typeid
所需的数据也可以与那些一起存储。
如果涉及到库,这类事情通常需要在库中公开这些类型信息结构,就像使用函数一样。例如,有可能获得一个链接器错误,看起来像“未定义引用'vtable for XXX'”(和男孩,那些令人讨厌的!),再次,就像功能一样。
答案 1 :(得分:13)
魔术。
开玩笑吧。如果你真的想详细研究它,那么为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
这详细说明了编译器生成的类型信息的格式,并且应该为您提供线索,然后运行时支持如何找到正确的转换路径。
答案 2 :(得分:2)
dynamic_cast如何知道Derived2是否派生自Derived(如果Derived是在另一个库中声明的话)?
dynamic_cast
本身并不知道任何东西,它是知道这些事实的编译器。 vtable不一定只包含指向虚函数的指针。
以下是我(天真地)这样做的方法:我的vtable将包含dynamic_cast
使用的某些类型信息(RTTI)的指针。类型的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;