static_cast
只能执行那些在编译时已知类之间的内存布局的强制转换。dynamic_cast
可以在运行时检查信息,这样可以更准确地检查强制转换的正确性,以及读取有关内存布局的运行时信息。
Base
虚拟继承将运行时信息放入每个对象中,该信息指定和之间的内存布局Derived
。是一个接一个,还是有额外的差距?由于static_cast
无法访问此类信息,编译器将采取保守行动,只会给出编译器错误。
更详细地说:
考虑一个复杂的继承结构,其中 - 由于多重继承 - 有多个Base
. 最典型的场景是钻石继承:
class Base {...};
class Left : public Base {...};
class Right : public Base {...};
class Bottom : public Left, public Right {...};
在这种情况下,Bottom
由Left
and组成Right
,其中每个都有自己的Base
. 以上所有类的内存结构在编译时都是已知的,static_cast
可以毫无问题地使用。
现在让我们考虑类似的结构,但具有虚拟继承Base
:
class Base {...};
class Left : public virtual Base {...};
class Right : public virtual Base {...};
class Bottom : public Left, public Right {...};
使用虚拟继承可确保在创建时,Bottom
它只包含在对象部分和. 对象的布局可以是例如:Base
Left
Right
Bottom
Base part
Left part
Right part
Bottom part
现在,考虑你投到Bottom
(Right
这是一个有效的投)。您获得一个Right
指向一个对象的指针,该对象分为两部分:Base
并且Right
在两者之间有一个内存间隙,包含(现在不相关的)Left
部分。有关此间隙的信息在运行时存储在Right
(通常称为vbase_offset
)的隐藏字段中。例如,您可以在此处阅读详细信息。
Right
但是,如果您只是创建一个独立的对象,则不会存在差距。
所以,如果我给你一个指针,Right
你在编译时不知道它是一个独立的对象,还是更大的东西的一部分(例如Bottom
)。您需要检查运行时信息才能正确地从Right
转换为Base
。这就是为什么static_cast
会失败和dynamic_cast
不会失败的原因。
关于 dynamic_cast 的注意事项:
虽然static_cast
不使用有关对象的运行时信息,但dynamic_cast
使用并要求它存在!因此,后一种转换只能用于那些包含至少一个虚函数(例如虚析构函数)的类