有关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错误的文章集结起来。最近几个月就写了不少。
参考资料
- 多继承的虚指针,lw02nju
- 浅议 Dynamic_cast 和 RTTI,RocZhang
RTTI,只支持多态类。dynamic_cast是运行时决议,也就是通过vptr[0]来查看类型信息,非多态的类本身没有vptr,所以肯定是不行的。我觉得《深度探索C++对象模型》中关于RTTI这一部分其实讲的很细致。
另外,思维你是用的什么编译器我用VC不支持
D* pD=dynamic_cast(new B1())这种写法。必须写成D* pD=dynamic_cast(new B1())
没错,其实就是你那一句话的解释。俺今天就在这糊涂半天,真是基础太差了,而且长久不用也忘掉一部分。回去复习深度探索COM了。
俺用的win下面的g++。我知道了,拷贝代码时候被吞掉了指定目标类型的部分,马上改。
The one beauty of doing your jewelry purchasing online is that you
are able This post will appeal to your interest > (Lyle) do it at your leisure and the time that fits
you and your schedule.