在具有共同祖先的类的指针之间进行转换是否合法?编译器是否注意到这种层次结构并确保其安全(调用1)? 或者用户是否必须手动完成层次结构才能始终安全(调用2)?
说我们有
class A{};
class B:A{};
class C:A
{
public:
int SomeFunc(){return 3;}
};
int _tmain(int argc, _TCHAR* argv[])
{
B* b = (B*)((A*)new C()); // this is done manually, i believe it is safe
((C*)b)->SomeFunc(); // is this safe? this is the cast in question
return ((C*)((A*)b))->SomeFunc(); // this is done manually, i believe it is safe
}
编辑:使此代码可编辑且可运行
edit2:添加了更多评论
答案 0 :(得分:2)
除非你真的知道自己在做什么,否则不要这样做。
强制转换是合法的,但在正确的类上使用它们会导致未定义的行为,任何使用b
而不进一步强制转换都会导致UB,这可能会起作用,什么也不做或者启动WWIII。
强制转换只是告诉编译器应该将变量视为另一种类型(除非它是多重继承),但是一旦使用了cast变量,它必须在代码的使用方式中合法使用它如果对象是C,则使用B的函数表是不好的,反之亦然。由于这是未定义的行为,编译器可能会发出它认为正确的代码。
实施例
class AA { };
class BB { };
class CC : public AA, public BB { };
int main () {
CC cc; // address is 0x22aa6f
BB* bb = &cc; // bb now is 0x22aa6f
cout << &cc << "," << bb << "\n";
return EXIT_SUCCESS;
}
给予
0x22aa6f,0x22aa6f
具有多重继承的示例
class AX{ int a = 1; };
class BX{ int b = 2; };
class CX:AX,BX {
public:
int c = 3;
int SomeFunc(){cout << "SomeFunc " << c << " "; return c;}
};
int cast() {
CX* c;
BX* b = (BX*)((AX*)(c = new CX())); // this is done manually, i believe it is safe
cout << "c=" << c << ", b=" << b << ", cx=" << ((CX*)b) << ", ca=" << ((CX*)((AX*)b)) << endl;
((CX*)b)->SomeFunc(); // is this safe? this is the cast in question
return ((CX*)((AX*)b))->SomeFunc(); // this is done manually, i believe it is safe
}
int main () {
return cast();
}
输出
c = 0x60003ac70,b = 0x60003ac70,cx = 0x60003ac6c,ca = 0x60003ac70
SomeFunc 2 SomeFunc 3
new
尽管地址错误,但对SomeFunc的2次调用仍然有效
this
指针this
,所以我必须添加一些用途。要检查它是否安全,您需要使用dynamic_cast。
int main() {
A* AP = new C();
C* CP = dynamic_cast<C*>(A);
if (CP != nullptr)
CP->SomeFunc();
return EXIT_SUCCESS;
}
答案 1 :(得分:2)
B* b = (B*)((A*)new C()); // this is done manually, i believe it is safe
这是不安全
大致来说,表单(T)
expr 的转换会转换为static_cast
或reinterpret_cast
。 [expr.cast] / 4:
执行的转化
- a
const_cast
(5.2.11),- a
static_cast
(5.2.9),- a
static_cast
后跟const_cast
,- a
reinterpret_cast
(5.2.10)或- a
reinterpret_cast
后跟const_cast
,可以使用显式类型转换的强制转换表示法执行。 应用相同的语义限制和行为 [...]如果a 转换可以用多种列出的方式解释 在上面,甚至使用列表中首先出现的解释 如果由该解释产生的演员表格不合格。
您可以在此忽略const_cast
,因为您的代码中未进行任何资格转换。
static_cast
在两个演员阵容中都足够,第一个,(A*)
和第二个,(B*)
。
第一个就好了。上传永远不是问题
第二个引发未定义的行为。 [expr.static.cast] / 11:
prvalue类型“指向 cv1
B
的指针”,其中B
是类类型, 可以转换为“指向 cv2D
的指针”的prvalue,其中 如果是有效标准,D
是B
派生的类(第10条) 存在从“指向D
”的指针到“指向B
的指针”的转换(4.10), cv2 与 cv1 具有相同的cv资格,或更高的cv资格,B
既不是D
的虚拟基类也不是基地D
的虚拟基类的类。 [...] 如果是类型的prvalue “指向cv1 B的指针”指向B
,它实际上是一个子对象 类型D
的对象,结果指针指向封闭D
类型的对象。否则,演员表的结果是未定义的。
另请注意,仅因为static_cast
触发UB并不意味着它未被选中(并被reinterpret_cast
替换)。
第二个和第三个演员表基于第一个(导致未定义的行为),因此谈论它们的有效性毫无意义。
答案 2 :(得分:1)
要检查演员表是否有意义,请使用dynamic_cast。如果强制转换是安全的,则dynamic_cast将正确转换或者如果它无法转换为目标类型,则返回NULL(如果是指针,则引用它会引发bad_cast异常)。
对于你的问题,只要想想这个演员是否有意义。您正在将B类投射到C,而这些类彼此之间并不了解。 所以,这个演员肯定会失败。
你在做: -
B* b = (B*)(new C());
如果给予dynamic_cast,这将失败(意味着甚至无法编译)因为涉及的类不是多态的。即使你使B类多态转换也会失败。进一步投射。
还可以安全地使用dynamic_cast进行交叉投射,假设类是多态的并且强制转换是安全的。例如: -
class A;
Class B;
Class C : public A, public B
A *a = new C;
您可以将其转换为兄弟姐妹: -
B *b = dynamic_cast<B*> (a);
答案 3 :(得分:0)
你的演员既合法又正确但非常危险。你应该使用reinterpret_cast&lt;&gt;在你的代码中标记它们。
您始终可以将任何类型A
的任何地址投射到任何类型B
的任何其他地址,并获取您的第一个地址。这基本上就是你所做的:
A *pa = &some_a;
B *pb = reinterpret_cast<B *>(pa);
pa = reinterpret_cast<A *>(pb);
然后取消引用pa
。这个例子有效,但很容易犯错误......