在我的一个项目中,我有一个实现四个接口的类:
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。
答案 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*
。