在两个派生类之间进行强制转换
casting between two derived classes
具有共同祖先的类的指针之间进行强制转换是否合法?编译器是否注意到这样的层次结构并确保其安全(调用 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:添加了更多评论
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(,- 一个
static_cast
后跟一个const_cast
,- a
reinterpret_cast
(5.2.10(,或- 一个
reinterpret_cast
后跟一个const_cast
,可以使用显式类型转换的强制转换表示法来执行。 相同的语义限制和行为适用 [...]
如果 可以通过列出的多种方式解释转换 上面,使用了列表中首先出现的解释,甚至 如果由该解释产生的石膏格式不正确。
您可以在此处忽略const_cast
因为代码中没有进行限定转换。 static_cast
两个演员阵容中都足够了,第一个,(A*)
,第二个,(B*)
。第一个很好。向上投射从来都不是问题。
第二个诱发未定义的行为。[expr.static.cast]/11:
类型为">指向 cv1 的指针"的 prvalue
B
,其中B
是类类型, 可以转换为"指向 CV2D
的指针"类型的 prvalue ,其中D
是从B
派生的类(第10条(,如果是一个有效的标准 存在从"指向D
的指针"到"指向B
的指针"的转换(4.10(, CV2 与 CV1 具有相同的 CV 资格,或比 CV1 更高的 CV 资格,并且B
既不是D
的虚拟基本类,也不是基础类D
的虚拟基类的类。[...]如果类型的 prvalue "指向 cv1 B 的指针"指向一个B
,该实际上是 类型D
的对象,生成的指针指向封闭 类型D
的对象。否则,转换的结果是未定义的。
另请注意,仅仅因为static_cast
触发了 UB,并不意味着它没有被选中(并替换为 reinterpret_cast
(。
第二个和第三个转换基于第一个(这会导致未定义的行为(,因此谈论它们的有效性是没有意义的。
除非你真的知道自己在做什么,否则不要那样做。
强制转换是合法的,但是在正确的类之外的任何东西上使用它们会导致未定义的行为,任何在没有进一步转换的情况下使用b
都会导致 UB,这可能有效、什么都不做或开始第三次世界大战。
只是告诉编译器应该将变量视为另一种类型(除非它是多重继承(,但是一旦使用了强制转换变量,以代码的方式使用它必须是合法的,如果对象是 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
- c 是
new
的真实地址
b 首先是转换为 AX,然后是 - c,然后转换为 BX(这对 AX 没有任何意义(,但 b 只是设置为与 c 相同的地址
- cx 被 b 重新解释为 CX,多重继承开始并真正更改地址,就好像 b 是继承中的第 2 类一样。
- ca 是通过 AX 和 CX 对 b 的正确重新解释。
尽管地址错误,但对 SomeFunc 的 2 次调用仍有效
- 函数调用是通过当前类型找到的,由于上次强制转换,该类型为 CX。
- 错误的地址作为指针传递
this
- 因为
this
没有使用它,所以我不得不添加一些用途。 - 现在我们已经输入了未定义的行为,因为强制转换(BX*(((AX*(c使强制转换(CX*(b做错了事情。
要检查它是否安全,您需要使用dynamic_cast。
int main() {
A* AP = new C();
C* CP = dynamic_cast<C*>(A);
if (CP != nullptr)
CP->SomeFunc();
return EXIT_SUCCESS;
}
要检查强制转换是否有意义,只需使用 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);
你的演员阵容既合法又正确,但非常危险。应使用 reinterpret_cast<> 在代码中标记它们。您始终可以将任何类型的A
的任何地址转换为任何类型的任何其他地址B
并取回您的第一个地址。这基本上是你所做的:
A *pa = &some_a;
B *pb = reinterpret_cast<B *>(pa);
pa = reinterpret_cast<A *>(pb);
然后取消引用pa
.这个例子有效,但很容易犯错误......
- 在 Rcpp 中的字符串类型之间转换时出错
- r-在Rcpp和C++之间转换矢量(使用Rcpp::as或Rcpp:::wrap)是否会创建一个新的矢量并复制元素
- 在不同类型之间转换常量指针
- 在 3D 骨架系统 DirectX 之间转换
- C++ 如何将用户控件添加到窗体,以便我可以在面板之间转换
- 在 std::u8string 和 std::string 之间转换
- 如何在Unicode/UCS CodePoint和UTF16替代对之间转换
- 在DXGI格式之间转换RGBA数据
- 在两个不动点表达之间转换
- 在函数指针类型之间转换
- 代码的错误答案是在Java Camel案件和C 下划线标识符之间转换的错误答案
- 如何在C 中的不同类之间转换
- 一个关于C++中double和int之间转换的奇怪结果
- 模板类在模板类型之间转换,但也专门化
- 是否有Visual c++编译器在线,以及如何在c++和vs简单代码之间转换
- c/在Unix时间和"Gregorian time"之间转换
- 无法使用iconv在编码之间转换
- 在std::string和std::wstring之间转换的多平台方式
- 我真的应该每次在基元类型之间转换时都使用static_cast吗
- 使用Boost::units在两个量之间转换的最简单方法