【问题标题】:How is dynamic_cast implementeddynamic_cast 是如何实现的
【发布时间】:2013-08-23 23:08:06
【问题描述】:

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

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

使用dynamic_cast<Derived*>(p) 可以尝试将Base* p 向下转换为Derived*。我曾经认为dynamic_cast 的工作原理是将p 中的vtable 指针与Derived 对象中的指针进行比较。

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

class Derived2 : public Derived { };

在这种情况下:

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

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

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

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

【问题讨论】:

  • 它可能在不同的编译器中实现不同,以确保您可能想阅读它们的源代码......

标签: c++ dynamic-cast


【解决方案1】:

魔法。

开个玩笑。如果你真的想详细研究这个,为 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.9.5 部分,其中包含 RTTI 结构(又名 v-table)的描述以及层次结构的编码方式(当然还有 2.9.7 描述算法本身)。
【解决方案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;

【讨论】:

  • 确实很幼稚,因为它没有考虑到多个基类、虚拟基类(那些不好玩)和偏移适应。基本的想法是好的:对象拓扑被编码在它的 V 表中。
  • @MatthieuM。这就是我想要的:描述基本思想。我想到了其他的东西(除了虚拟基类,把它们搞砸!)但没有费心编写大量无用的伪代码 ;-)
  • 对于参考实现,我在libcxxrt 中发现了一个非常可读的实现,结合来自Sebastian Redl 的链接,即RTTI 结构的布局在第2.9.5 节中的布局尤其清晰安腾 ABI。
  • @MatthieuM。也很聪明。这个想法实际上与这里提出的想法相似,但使用多态递归实现以支持从 __class_type_info 派生的不同类型的类。感谢您的链接!
【解决方案3】:

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

这个问题的答案非常简单:dynamic_cast 可以通过保留这些知识来知道这一点。

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

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

【讨论】:

  • 涉及多重继承时会出现一些并发症。参见例如this question
猜你喜欢
  • 2011-03-19
  • 2011-06-20
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 1970-01-01
相关资源
最近更新 更多