多重继承转换未按预期工作
Multiple inheritance cast not working as expected
我最近遇到了强制转换和多重继承的问题:我需要将 Base*
强制转换为 Unrelated*
,因为特定的 Derived
class 派生了 Unrelated
class.
这是一个简短的例子:
#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
不应该只适用于转换为相关的 class 类型吗?我认为在运行时没有关于 Unrelated
的任何信息,因为它不是多态的。
编辑:我使用 colirus gcc 作为示例。
dynamic_cast
之所以有效,是因为指向其基址 class 的指针指向的对象的 动态类型 与 Unrelated
相关。
请记住,dynamic_cast
需要虚拟 table 在 运行 时检查对象的继承树。
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 风格转换的同事偶尔也会犯错。
第一个转换 (Unrelated*)b
不起作用,因为您将 Base
class 子对象(可能只包含一个 vtable 指针)视为 Unrelated
, 包含 float
.
相反,您可以向下和向上投射,static_cast<Unrelated*>( static_cast<Derived*>( b ) )
。
这就是 dynamic_cast
为您所做的,因为 Base
是一种多态类型(至少一个虚拟方法),它允许 dynamic_cast
检查最派生的类型对象。
顺便说一下,dynamic_cast<void*>( b )
会给你一个指向最派生对象的指针。
但是,由于您知道类型,因此无需调用 dynamic_cast
的轻微开销:只需进行向下和向上转换。
您应该使用相应的 C++ named cast 或强制转换,而不是 C 风格的 cast (Unrelated*)b
,因为 C 风格指针转换 可以做一些你意想不到的事情,因为在维护期间更改类型时效果可能会完全改变。
C 风格转换将最多执行 2 个 C++ 命名转换。在这种情况下,C 样式转换对应于 reinterpret_cast
。编译器不允许在此处进行任何其他命名转换。
这是一个警告标志。 ;-)
相比之下,向下和向上转换是 static_cast
,通常是良性转换。
综上所述,最好的方法是使用绝密技术几乎完全避免强制转换:
首先不要丢弃类型信息。
即在示例代码中,仅使用 Derived*
作为指针的类型。
通过多重继承,Derived
对象由两个子对象组成,一个Base
和一个Unrelated
。编译器知道如何访问它需要的对象的任何部分,通常是通过向指针添加偏移量(但这是一个实现细节)。当它知道指针的实际类型时,只能这样做。通过使用 C 风格的转换,您已经告诉编译器忽略实际类型并将该指针值视为指向其他内容的指针。它不再具有正确访问所需子对象所需的信息,因此失败。
dynamic_cast
允许编译器使用有关对象的 运行 时间信息来定位包含在其中的正确子对象。如果您要输出或检查指针值本身,您会发现它们是不同的。
我最近遇到了强制转换和多重继承的问题:我需要将 Base*
强制转换为 Unrelated*
,因为特定的 Derived
class 派生了 Unrelated
class.
这是一个简短的例子:
#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
不应该只适用于转换为相关的 class 类型吗?我认为在运行时没有关于 Unrelated
的任何信息,因为它不是多态的。
编辑:我使用 colirus gcc 作为示例。
dynamic_cast
之所以有效,是因为指向其基址 class 的指针指向的对象的 动态类型 与 Unrelated
相关。
请记住,dynamic_cast
需要虚拟 table 在 运行 时检查对象的继承树。
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 风格转换的同事偶尔也会犯错。
第一个转换 (Unrelated*)b
不起作用,因为您将 Base
class 子对象(可能只包含一个 vtable 指针)视为 Unrelated
, 包含 float
.
相反,您可以向下和向上投射,static_cast<Unrelated*>( static_cast<Derived*>( b ) )
。
这就是 dynamic_cast
为您所做的,因为 Base
是一种多态类型(至少一个虚拟方法),它允许 dynamic_cast
检查最派生的类型对象。
顺便说一下,dynamic_cast<void*>( b )
会给你一个指向最派生对象的指针。
但是,由于您知道类型,因此无需调用 dynamic_cast
的轻微开销:只需进行向下和向上转换。
您应该使用相应的 C++ named cast 或强制转换,而不是 C 风格的 cast (Unrelated*)b
,因为 C 风格指针转换 可以做一些你意想不到的事情,因为在维护期间更改类型时效果可能会完全改变。
C 风格转换将最多执行 2 个 C++ 命名转换。在这种情况下,C 样式转换对应于 reinterpret_cast
。编译器不允许在此处进行任何其他命名转换。
这是一个警告标志。 ;-)
相比之下,向下和向上转换是 static_cast
,通常是良性转换。
综上所述,最好的方法是使用绝密技术几乎完全避免强制转换:
首先不要丢弃类型信息。
即在示例代码中,仅使用 Derived*
作为指针的类型。
通过多重继承,Derived
对象由两个子对象组成,一个Base
和一个Unrelated
。编译器知道如何访问它需要的对象的任何部分,通常是通过向指针添加偏移量(但这是一个实现细节)。当它知道指针的实际类型时,只能这样做。通过使用 C 风格的转换,您已经告诉编译器忽略实际类型并将该指针值视为指向其他内容的指针。它不再具有正确访问所需子对象所需的信息,因此失败。
dynamic_cast
允许编译器使用有关对象的 运行 时间信息来定位包含在其中的正确子对象。如果您要输出或检查指针值本身,您会发现它们是不同的。