导致SIGILL的C风格转换,dynamic_cast确定

时间:2011-11-17 15:38:42

标签: c++ g++

在我的一个项目中,我有一个实现四个接口的类:

class A : public B,
          public C,
          public D,
          public E
{
  ----Implementation Code here----
};

这四个接口只包含纯虚函数,它们都不构成菱形问题(所以我不需要使用虚拟关键字)所以我做了这样的事情时没有问题:

A* var = new A;
((C*)var)->method_from_interface();

然而,正在发生一些丑陋的事情,因为该函数正在跳转到A类的不同方法,而valgrind抱怨未处理的指令。但是,这样做:

 A* var = new A;
(dynamic_cast<C*>(var))->method_from_interface();

可疑。

所以我想知道这是一个G ++错误还是一些误用语言?

编辑:

也许我已经简化了我的问题。我在函数调用中收到A类作为D *:

void do_something(provider* p) {
   D* iface = p->recoverItemByName("nameThatReturnsClassA");
   ((B*) iface)->call_method_from_b_iface();
}

请注意,我知道在这个时候D *实际上是一个A *,所以对B *进行投射不会破坏任何规则。我不能static_cast它,因为D *和B *没有关系,但我可以成功使用reinterpret_cast。

3 个答案:

答案 0 :(得分:3)

C风格的强制转换只是对指向类的指针进行“基本”内存映射。如果你的方法在你的类中偏移= 42,那么它会产生(*(A + 42))()(当然简化)。

但是,如果从多个类继承,则必须考虑编译器放置它们的各种类和顺序。

static_cast考虑了多重继承。

在你的例子中,它可能适用于B,C,D或E,但不适用于其他的。但是,你没有充分的理由这样做:你会打电话给A->methodFromInterface(),它就可以了!

在C ++上,建议使用static_cast或dynamic_cast(注意:第二个依赖于可能不可用的rtti)并丢弃旧的C风格的演员。

修改的 试图重现问题,但不能,文件可以在

找到

在MacOs 10.6上编译:

g++ -fno-rtti -o truc ./class.cpp ./class2.cpp && ./truc

答案 1 :(得分:2)

问题在于你的函数do_something。

这条线很好:

D* iface = p->recoverItemByName("nameThatReturnsClassA");

但这条线很糟糕,因为B与D无关,所以你无法安全施放。尝试调用static_cast(iface),你会看到编译器抱怨 - 这应该是对你的警告。

((B*) iface)->call_method_from_b_iface(); 

在上面一行中,您编写的知识是,iface不仅指向D,而且还指向A.更好地存储指向A的指针并使用该指针。

A* iface = p->recoverItemByName("nameThatReturnsClassA");
iface->call_method_from_b_iface(); 

如果你仍然想坚持使用D * iface,那么就像这样编码(只有两个选项):

D* iface = p->recoverItemByName("nameThatReturnsClassA");
static_cast<A*>(iface)->call_method_from_b_iface();  // option 1, easy to understand
dynamic_cast<B*>(iface)->call_method_from_b_iface(); // option 2, will do a cross-cast

有关dynamic_cast及其交叉投射功能的更多信息,请参阅http://msdn.microsoft.com/en-us/library/cby9kycs.aspx

最后但并非最不重要:尝试避免使用旧的C风格转换,并始终尝试使用C ++样式转换const_cast,static_cast / dynamic_cast和reinterpret_cast(按此顺序)。

答案 2 :(得分:2)

  

请注意,我知道在这个时候D *实际上是A *,所以铸造   对B *进行投射并没有违反任何规则。

错误。

可能知道它实际上是一个A *,但是编译器没有,至少在编译时它试图弄清楚要发出什么代码来进行转换。

当您使用虚方法从多个类继承时,会发生一件有趣的事情。当您从派生类转换为其中一个接口类时,指针地址会发生变化!编译器会对指针进行调整,使其对您声明的指针类型有效。自己尝试一下,从A*指针开始并显示其值,然后转换为B*C*D*并显示它们。最多一个基类与A*相同,其他基类不同。

当你使用C风格的演员表时,你告诉编译器“我不在乎你是否不能正确地进行转换,无论如何都要这样做。”它适当地将D*视为B*而不进行必要的修正,所以现在指针完全错误。它没有指向B类vtable,因此调用了错误的方法。

dynamic_cast之所以有效,是因为它在运行时使用了额外的信息;它可以跟踪指向最派生A*的指针,然后返回B*