我最近遇到了强制转换和多重继承的问题:我需要将Base*
强制转换为Unrelated*
,因为特定的Derived
类派生了Unrelated
类。< / p>
这是一个简短的例子:
#include <iostream>
struct Base{
virtual ~Base() = default;
};
struct Unrelated{
float test = 111;
};
struct Derived : Base,Unrelated{};
int main(){
Base* b = new Derived;
Unrelated* u1 = (Unrelated*)b;
std::cout << u1->test << std::endl; //outputs garbage
Unrelated* y = dynamic_cast<Unrelated*>(b);
std::cout << y->test << std::endl; //outputs 111
}
第一个演员显然不起作用,但第二个演员确实有效。
我的问题是:为什么第二次演员工作? dynamic_cast
只能用于转换为相关的类类型吗?我认为在运行时没有关于Unrelated
的任何信息,因为它不是多态的。
编辑:我使用colirus gcc作为示例。
答案 0 :(得分:8)
dynamic_cast
有效,因为指向其基类的指针指向的动态类型与Unrelated
相关。
请记住,dynamic_cast
需要一个虚拟表来在运行时检查对象的继承树。
C风格的演员(Unrelated*)b
不起作用,因为the C-style cast does const_cast
, static_cast
, reinterpret_cast
and more, but it does not do dynamic_cast
。
我建议在C ++代码中避免使用C风格的强制转换,因为它们做了很多事情而不是精确的C ++强制转换。我坚持使用C风格演员的同事偶尔会误解他们。
答案 1 :(得分:5)
第一个强制转换(Unrelated*)b
不起作用,因为您正在将Base
类子对象(可能只包含一个vtable指针)视为Unrelated
,其中包含{{ 1}}。
相反,你可以向下和向下,float
。
这就是static_cast<Unrelated*>( static_cast<Derived*>( b ) )
为你做的事情,因为dynamic_cast
是一个多态类型(至少一个虚方法),它允许Base
检查最派生对象的类型。
顺便说一句,dynamic_cast
会给你一个指向最派生对象的指针。
但是,既然您知道类型,则无需调用dynamic_cast<void*>( b )
的轻微开销:只需执行向下和向上转换。
取代 C样式 dynamic_cast
,您应该使用相应的C ++ 命名强制转换或强制转换,因为C样式强制转换指针可以做你不想要的事情,因为在维护期间更改类型时效果会完全改变。
C样式转换将最多执行2个C ++命名转换。在这种情况下,C样式转换对应于(Unrelated*)b
。编译器不允许在此处进行任何其他命名转换。
这是一个警告标志。 ; - )
相比之下,向下和向上演员都是reinterpret_cast
s,通常是良性演员。
所有这一切,最好是通过使用绝密技术几乎完全避免演员阵容:
首先不要丢弃类型信息。
即,在示例代码中,只使用static_cast
作为指针的类型。
答案 2 :(得分:2)
对于多重继承,Derived
对象由两个子对象组成,一个Base
和一个Unrelated
。编译器知道如何访问所需对象的任何部分,通常是通过向指针添加偏移量(但这是一个实现细节)。当它知道指针的实际类型时,它只能 执行该操作。通过使用C样式转换,您已告诉编译器忽略实际类型并将该指针值视为指向其他内容的指针。它不再具有正确访问所需子对象所需的信息,并且失败了。
dynamic_cast
允许编译器使用有关该对象的运行时信息来定位其中包含的正确子对象。如果您要输出或检查指针值本身,您会发现它们不同。