在一个头文件中,我有:
#include "BaseClass.h"
// a forward declaration of DerivedClass, which extends class BaseClass.
class DerivedClass ;
class Foo {
DerivedClass *derived ;
void someMethod() {
// this is the cast I'm worried about.
((BaseClass*)derived)->baseClassMethod() ;
}
};
现在,DerivedClass是(在它自己的头文件中)从BaseClass派生的,但编译器当时不知道它正在读取类Foo的上面定义。但是,Foo引用DerivedClass指针而DerivedClass引用Foo指针,因此它们不能同时知道彼此的声明。
第一个问题是,在缺少派生类的完整定义的情况下,将派生类指针强制转换为基类指针类型是否安全(根据C ++规范,而不是在任何给定的编译器中)。
第二个问题是,是否有更好的方法。我知道我可以将someMethod()的主体移出类定义,但在这种情况下,重要的是它被内联(实际的,测量的热点的一部分 - 我不猜)。
答案 0 :(得分:3)
它可能有用,但风险很大。
问题在于,Derived*
和Base*
的大部分时间确实具有相同的值(您可以打印)。但是,只要您具有虚拟继承和多继承,就不是这样,并且标准肯定不能保证这一点。
使用static_cast
时,编译器会执行必要的算术运算(因为它知道类的布局)以调整指针值。但是这种调整不是由reinterpret_cast
或C风格演员表演的。
现在,您可以完美地修改代码:
// foo.h
class Derived;
class Foo
{
public:
void someMethod();
private:
Derived* mDerived;
};
// foo.cpp
#include "myProject/foo.h"
#include "myProject/foo.cpp"
void Foo::someMethod() { mDerived->baseClassMethod(); }
无论如何:你正在使用一个指向Derived
的指针,虽然你可以知道有很多陷阱:值得注意的是,如果你想要new
一个类的实例它将是您需要重新定义类的Copy Constructor
,Assignment Operator
和Destructor
以正确处理指针。还要确保在每个Constructor
中初始化指针值(无论是NULL还是Derived
的实例)。
这不是不可能,而是记录下来。
答案 1 :(得分:3)
你应该使用c ++样式转换,而不是c风格。原因是你正在做的事实上是一个reinterpret_cast,它缺少一些罕见的用途,几乎普遍不安全。
你正在表演的演员阵容没有正确完成。您可以安全地在两者之间进行转换,这是实现定义的(只有void *由标准保证能够保存指向任何类型的指针),但如果您实际上使用了新指针,那么您将召唤鼻子恶魔。
大多数情况下,这些鼻腔恶魔是良性的,你不会注意到它们(在相当多的实施中)。但是,如果你使用多重继承,这些相同的鼻子将变得非常狡猾。即便如此,你也不应该接受未定义的行为作为“安全”或除了本身不好之外的任何其他行为,除非根本没有其他方法可以得到你需要的东西(作为一个我从未遇到过的专业人士)。
这引出了不使用c风格演员表的全部理由。当你需要时,有一些非常非常罕见的条件,但作为一个专业人士,我从来没有遇到过。 c样式转换的问题在于它们将根据情境的语义静默地更改为不同类型的转换。在不同的代码区域中,轻微的代码更改可能会在您不知情的情况下严重改变转换操作。当语义变为需要不同类型的强制转换时,c ++样式强制转换将通知您,c样式强制转换将简单地无声地更改;当使用c风格的强制转换时,您可以从定义到未定义的行为,而不会发生任何形式的线索。
答案 2 :(得分:2)
你在这里用C ++的术语reinterpret_cast
(因为“已知安全”static_cast
会导致编译错误),我会非常怀疑。
是不是可以转发声明Foo for Derived?