失败和格式错误的强制转换
Failed and ill-formed casts
你能解释一下ill-formed cast
和failed cast
的区别吗?例如:
class A { virtual void f(); };
class B { virtual void g(); };
class D : public virtual A, private B { };
void g() {
D d;
B* bp = (B*)&d; // cast needed to break protection
A* ap = &d; // public derivation, no cast needed
D& dr = dynamic_cast<D&>(*bp); // fails
ap = dynamic_cast<A*>(bp); // fails
bp = dynamic_cast<B*>(ap); // fails
ap = dynamic_cast<A*>(&d); // succeeds
bp = dynamic_cast<B*>(&d); // ill-formed (not a run-time check)
}
不管名字如何,当您使用dynamic_cast
进行上强制转换(派生->基)时,强制转换是在编译时完成的,其行为方式与static_cast
或隐式转换相同—如果基是不明确的或不可访问的,则程序是病态的,这意味着编译器必须产生诊断。§5.2.7 [expr.dynamic.cast]/p5:
[对于表达式
dynamic_cast<T>(v)
:]如果
T
是"指向cv1 B
的指针"且v
是"指向cv2 D
的指针"类型因此,B
是D
的基类,其结果是指向v所指向的D
对象的唯一B
子对象T
是"对cv1 B
的引用",v
具有cv2 D
类型,因此B
是D
的基类,结果是唯一的B
子对象v
所引用的D
对象。67结果为左值如果T
是左值引用,如果T
是右值引用,则为xvalue引用参考。在指针和引用的情况下,程序都是如果cv2
具有比cv1
更大的cv- qualified,或者如果B
是D
的不可访问或有歧义的基类。67指向或引用的最派生对象(1.8)v可以包含其他B对象作为基类,但这些会被忽略。
在其他情况下(向下或横向转换),检查在运行时执行。如果转换失败,则转换结果是指针类型转换的空指针,引用类型转换的std::bad_cast
异常。
让我们从头开始,看看每一种情况:
class A { virtual void f() {} };
class B { virtual void g() {} };
class D : public virtual A, private B { };
void g() {
D d;
B* bp = (B*)&d;
[continue...]
c++类型转换(reinterpret_cast
例外,它只是转换原始指针值,根本没有调整或算术)不允许"忽略"继承访问级别(https://stackoverflow.com/a/3674955/1938163), C风格的类型转换可以。
bp
是一个指向有效对象的指针,但是完全绕过了访问级别。这有问题吗?答案是肯定的,以以下例子为例:
class A { virtual void f() {} };
class B { virtual void g() {}
public:
~B() {cout << "B's destructor";} // You can destroy B objects but NOT D objects from B* pointers
};
class D : public virtual A, private B {
~D() {cout << "D's destructor";}
};
void g() {
D *d = new D();
B* bp = (B*)d; // Bypass access permissions
delete bp; // This shouldn't happen! D's destructor will NOT be called! Undefined Behavior!
使用-Wold-style-cast -Werror
编译可以避免这个问题(以及其他几个问题:https://stackoverflow.com/a/12765440/1938163)
继续刚才的例子,我们有
A* ap = &d;
,这是一个完全合法的向上转换。不合法的是以下类型转换:
D& dr = dynamic_cast<D&>(*bp);
从标准中引证并替换一些词以方便阅读:
因此(N3690 -§5.2.7 - 8)
如果D是类类型,D&点或引用,运行时检查逻辑执行如下:如果在bp指向(引用)的最派生对象中,bp指向(引用)一个公共基类如果只有一个D类型的对象是从指向(引用)的子对象派生出来的
将结果指向D对象。
访问权限是错误的,强制转换失败。不是格式错误,只是失败了(稍后阅读以了解差异)。
如果你要求一个指针,你会得到一个NULL
的指针,但是因为引用需要绑定到一个对象,上面抛出一个异常(这是这里唯一明智的事情)。
后面的强制转换也不是错误的,而是完全错误的。通常你不能从基指针强制转换到另一个基指针(https://stackoverflow.com/a/7426562/1938163),但因为基这里涉及的类是多态的,下面的操作应该是允许的并且是有效的:
ap = dynamic_cast<A*>(bp);
…如果bp指向(引用)最派生对象的公共基类子对象,则最派生对象的类型具有类型a的基类,该基类是明确且公开的,即结果指向最派生对象的A子对象。
但再次:权限出错,强制转换失败(不是格式错误,再次失败)。
后面的强制转换已经是无效的强制转换,因为ap
是NULL
bp = dynamic_cast<B*>(ap);
但是如果ap
不是NULL
,对于上面引用的相同段落,强制转换无论如何都会失败:
…如果ap指向(引用)最派生对象的公共基类子对象,则最派生对象的类型有一个B类型的基类,该基类为明确,结果为public指向最派生对象的B子对象。
唯一成功的强制转换是
ap = dynamic_cast<A*>(&d);
其中D
对象指针被强制转换为公共基类。
最后一次强制转换最终格式错误
bp = dynamic_cast<B*>(&d);
因为根据标准
(N3690 -§5.2.7 - 5)
(dynamic_cast)如果B*是"指向cv1 B的指针",并且&d的类型是"指向cv2 d的指针",使得B是d的基类,则结果是指向&d所指向的d对象的惟一的B子对象的指针。
…
在指针和引用的情况下,如果cv2比cv1具有更大的cv限定条件,程序是病态的,或者如果B是D的不可访问或有歧义的基类,程序是病态的。
Grand-total: 3次强制转换失败(一次抛出异常),一次成功,一次错误。
最后:失败的强制转换是指无法完成但可以根据标准规则处理的强制转换:
如果v的值是指针情况下的空指针值,则结果为T类型的空指针值。如果C是T指向或引用的类类型,则运行时检查在逻辑上按如下方式执行:
…(规则同上)
—否则,运行时检查失败。
转换为指针类型失败的值是所需结果类型的空指针值。一个失败的强制转换为引用类型抛出异常
除非另有指示(不需要诊断),否则编译器实现通常应该对格式错误的程序发出错误或警告,在您感兴趣的强制转换中:
B* bp = (B*)&d;
A* ap = &d;
D& dr = dynamic_cast<D&>(*bp); // This is a runtime error
ap = dynamic_cast<A*>(bp); // this is a runtime error
bp = dynamic_cast<B*>(ap); // this is a runtime error
ap = dynamic_cast<A*>(&d); // succeeds
bp = dynamic_cast<B*>(&d); // This is ill-formed and the compiler should warn about it
如果我有什么错(很可能)请写下来在下面的评论,我会立即修复我的帖子。谢谢!
- C++使用params创建线程函数会导致转换错误
- 为什么g++在未执行的代码处标记强制转换错误
- 为什么C++在将浮点数转换为字符时没有显示缩小转换错误?
- 如何在构建对象堆栈时解决转换错误?
- 执行具有转换错误的过程
- 指向类成员函数的指针中存在类型转换错误
- 为什么我在这里收到C++没有已知的转换错误?
- 增加图形转换错误
- 尝试向 COM 对象添加另一个接口时出现静态强制转换错误 C2440
- 返回带有另一个类的数据成员的构造函数?遇到转换错误?
- 指针和双重转换错误,代码没有
- 自定义函数转换错误?
- 将 C 程序转换为具有大量字符* 和无符号字符转换错误的C++
- 如何在我使用过的Arduino库之一中固定构造函数中的转换错误
- 试图使用strCMP时的char转换错误
- 我在 TSP 的C++解决方案中遇到转换错误
- "double(*)(string, double, double)"尝试传递函数C++时"double"转换错误
- 此语法中的构造函数转换错误
- Opengl 精度转换错误编译错误 E0415
- 无效的转换错误和无效的类型