繁体   English   中英

失败和格式错误的演员表

[英]Failed and ill-formed casts

您能否解释一下ill-formed castfailed 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转换在编译时完成,其行为与static_cast或隐式转换相同-如果基础不明确或无法访问,则程序异常格式,表示编译器必须产生诊断信息。 §5.2.7[expr.dynamic.cast] / p5:

[对于表达式dynamic_cast<T>(v) :]

如果T是“指向cv1 B指针”并且v具有类型“指向cv2 D指针”,使得BD的基类,则结果是指向v指向的D对象的唯一B子对象的指针。类似地,如果T是“对cv1 B引用”并且v具有cv2 D类型,使得BD的基类,则结果是v引用的D对象的唯一B子对象。 67其结果是左值如果T是一个左值参考,或x值如果T是一个rvalue参考。 在指针和引用两种情况下,如果cv2 cv资格大于cv1的资格,或者BD的不可访问或模棱两可的基类,则程序是cv2

67 v指向或引用的最派生对象(1.8)可以包含其他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例外,它仅强制转换原始指针值而无需进行任何调整或算术运算)不允许使用C样式“忽略”继承访问级别( https://stackoverflow.com/a/3674955/1938163 )演员可以。

使用上面的代码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 : -Wold-style-cast -Werror

继续您的示例,我们有

A* ap = &d;

这是完全合法的选择。 以下是不合法的演员表:

D& dr = dynamic_cast<D&>(*bp);

出于可读性原因,引用标准并替换一些单词:

(N3690-§5.2.7-8)

如果D是D&指向或引用的类类型,则运行时检查在逻辑上执行如下:—如果在bp指向(引用)的最派生对象中bp指向(引用) 公共基类的子对象D对象,并且如果仅从bp指向(引用)的子对象派生出类型D的一个对象,则指向该D对象的结果点(引用)。

因此访问权限错误,并且转换失败。 不是格式错误,只是失败(稍后阅读以了解区别)。

如果您要求一个指针,您将得到一个NULL ,但是由于引用需要绑定到对象,因此上面的方法会引发异常(这是在这里做的唯一明智的事情)。

随后的演员表也并非格式错误,只是明显的错误。 通常,您不能从基址指针强制转换为另一个基址( https://stackoverflow.com/a/7426562/1938163 ),但由于此处涉及的基类是多态的,因此应允许以下内容并有效:

ap = dynamic_cast<A*>(bp);

...如果bp指向(引用)最派生对象的公共基类子对象,并且最派生对象的类型具有类型A的基类,即明确且公共,则结果指向(引用)到最派生对象的A子对象。

但同样:权限使事情出错,并且转换失败( 不是格式错误,再次只是失败)。

由于apNULL ap已经是无效的转换

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的cv资格大于cv1的资格,或者B是D的不可访问或模棱两可的基类则程序是错误的。

总计: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

如果我出错了(很可能),请在下面的评论中写下来,我会立即修复。 谢谢!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM