有关dynamic_cast<>的可用性(基础差害死人)

一直以来,有关dynamic_cast<>这个转换所读到的书和材料消化后,都觉得结论是“拥有带有继承关系的对象指针转换,安全,会抛异常”。但事实上真的如此吗?看下面的代码:

class B1{virtual void foo(){}};
class B2{};
class D{};
D* pD=dynamic_cast(new B1()); // (1)
cout<<*(int*)(&pD)<(new B2()); // (2)
cout<<*(int*)(&pD2)<

可以看到B1、B2和D三个类没什么继承关系,但(1)处通过编译,(2)处提示:

cannot dynamic_cast `(((main()::B2*)operator new(1u)), (((*)  main()::B2()), ))' (of type `class main()::B2*') to type `class main()::D*' (source type is not polymorphic) 

即来源类型非多态。所以实际上并非是“有继承关系的对象指针”可用dynamic_cast<>转换,而是有vtable的类都可以转——只不过没继承关系的话转出来为NULL。
那么再想一下,既然提示了“多态”,也就是说关键点不在“继承”这里,所以形如下面的代码:

class X {};
class Y : X {};
Y* y = dynamic_cast(new X()); // failed!

虽然两个类有继承关系,但必然通不过编译,因为基类子类并未表现出多态,编译器不会多费劲来生成vtable。在文章多继承的虚指针中提到了dynamic_cast的内部实现:

void *FindCompleteObject (void *ptr )
{
     const _s_RTTICompleteObjectLocator *pCompleteLocator =GetCompleteObjectLocator (ptr );
     ptr =ptr -pCompleteLocator ->offset ;
     if (pCompleteLocator ->cdOffset ) ptr -=*(ptr -pCompleteLocator ->cdOffset );
     return ptr ;
}
void *__RTCastToVoid (void *ptr )
{
     if (!ptr ) return NULL ;
     return FindCompleteObject (ptr );
}

可以看到只有vtable存在,寻找Complete Object Locator的行为才有意义——虚表的第一项就是RTTI Complete Object Locator(RTTI Info),其中包含RTTI类型描述(Type Descriptor)、RTTI继承关系(Class Hierarchy)[2]。故言C++的RTTI特性也是以vtable的存在为基础,没有虚函数或其他virtual特性,RTTI不生效。
当然一般使用中其实理解成dynamic_cast<>用于向下转换带有继承关系的指针也没错,上面提到的这两个特例应该很少会出现在实际代码中吧。但此处犯错足见俺基础太差,离C++对象模型的深入理解还差得远。
p.s:真想把之前这类因为基础差犯2B错误的文章集结起来。最近几个月就写了不少。

参考资料

  1. 多继承的虚指针,lw02nju
  2. 浅议 Dynamic_cast 和 RTTI,RocZhang