失败和形成不良的演员

时间:2014-08-03 08:07:49

标签: c++ casting

您是否可以解释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)
}

2 个答案:

答案 0 :(得分:4)

尽管有名称,当你使用dynamic_cast进行upcast(derived-&gt; base)时,强制转换是在编译时完成的,其行为与static_cast或隐式转换的行为相同 - 如果基础不明确或不可访问,则程序格式错误,这意味着编译器必须生成诊断。 §5.2.7[expr.dynamic.cast] / p5:

  

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

     

如果T是“指向cv1 B的指针”而v的类型为“指向cv2 D的指针”   这样BD的基类,结果是指向的B   v指向的D对象的唯一T子对象。同样,如果   cv1 B是“v的引用”,而cv2 D的类型为BD   是B的基类,结果是唯一的D子对象   v引用的T对象。 67 结果是左值   如果T是左值引用,或者如果cv2是右值,则为xvalue   参考。在指针和参考情况下,程序是   如果cv1的cv资格高于BD,则格式不正确   是std::bad_cast的无法访问或模糊的基类。

     

67 指向或引用的最派生对象(1.8)   v可以包含其他B对象作为基类,但这些对象将被忽略。

在其他情况下(向下或向侧面),检查在运行时执行。如果失败,则转换结果是指针强制转换的空指针,以及参考强制转换的{{1}}异常。

答案 1 :(得分:1)

让我们从头开始,看看每个案例:

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&amp; D的班级类型。 point或refer,运行时检查在逻辑上执行如下:    - 如果在bp指向(引用)的最派生对象中,bp指向(指)公共基类   D对象的子对象,如果只有一个D类对象是从指向(引用)的子对象派生的   通过bp,结果指向(引用)该D对象。

因此访问权限是错误的,并且转换失败。它是 NOT 格式不正确,只是失败(稍后会读到差异)。

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

随后的演员阵容也不是格式错误,而是完全错误的。通常你不能从基指针转换到另一个基(https://stackoverflow.com/a/7426562/1938163)但是从基数开始 这里涉及的类是多态的,应该允许以下有效:

ap = dynamic_cast<A*>(bp);
  

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

但又一次:权限出错了,演员表失败了( NOT 格式不正确,再次失败)。

由于apNULL

,所以后面的演员阵容已经无效
bp = dynamic_cast<B*>(ap);

但如果ap不是NULL,那么无论如何,对于上面引用的相同段落,演员都会失败:

  

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

唯一成功的演员是

ap = dynamic_cast<A*>(&d);

其中D对象指针被转换为公共基类。

最后一次演员终于格式错误

bp = dynamic_cast<B*>(&d);

因为按照标准

  

(N3690 - §5.2.7 - 5)

     

(dynamic_cast的)

     

如果B *是“指向cv1 B的指针”并且&amp; d具有类型“指向cv2 D的指针”,使得B是D的基类,则结果是指向D对象的唯一B子对象的指针通过&amp; d。

     

...

     

在指针和参考情况下,程序格式错误如果cv2具有比cv1更高的cv资格,或者如果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

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