无效C样式转换后的方法调用

时间:2016-07-27 09:17:55

标签: c++ c++11 dynamic-cast static-cast

所以我正在尝试更多地了解 C-style-casts static_cast 之间的差异 dynamic_cast 我决定尝试这个应该反映 C-style-casts 之间差异的示例和 static_cast 非常好。

class B
{
public:
    void hi() { cout << "hello" << endl; }
};

class D: public B {};

class A {};

int main()
{
    A* a = new A();
    B* b = (B*)a;
    b->hi();
}

这段代码片段应该反映 C-style-cast 非常错误,并且根本检测不到错误的演员表。部分地发生这种情况。没有检测到糟糕的演员阵容,但是我很惊讶程序,而不是在b->hi();崩溃,它在屏幕上打印出“你好”这个词。

现在,为什么会这样?当没有实例化B对象时,用什么对象来调用这样的方法?我正在使用g ++进行编译。

3 个答案:

答案 0 :(得分:3)

正如其他人所说,这是不确定的行为。

为什么它有效?这可能是因为函数调用在编译时是静态链接的(它不是虚函数)。函数B::hi()存在,因此被调用。尝试将变量添加到class B并在函数hi()中使用它。然后你会在屏幕上看到问题(垃圾值):

class B
{
public:
    void hi() { cout << "hello, my value is " << x << endl; }

private:
    int x = 5;
};

否则你可以使函数hi()成为虚拟的。然后函数在运行时动态链接,程序立即崩溃:

class B
{
public:
    virtual void hi() { cout << "hello" << endl; }
};

答案 1 :(得分:1)

  

现在,为什么会发生这种情况?

因为它可能发生。任何事情都可能发生。行为未定义

事情发生了意想不到的事实,说明为什么UB如此危险。如果它总是导致崩溃,那么处理起来会容易得多。

  

使用什么对象来调用这样的方法

最有可能的是,编译器盲目地信任您,并假设b确实指向类型为B的对象(它没有)。它可能会使用指向的内存,就好像假设是真的一样。成员函数不访问属于该对象的任何内存,并且行为恰好与存在正确类型的对象相同。

未定义,行为可能完全不同。如果你试图再次运行程序,标准并不能保证恶魔不会飞出你的鼻子。

答案 2 :(得分:1)

这只能起作用,因为<DataTemplate DataType="{x:Type local:MyViewModel}"> <!-- xaml is typed here directly --> <Border> ... </Border> </DataTemplate> 方法本身的实现,以及C ++规范的特殊部分称为未定义的行为

使用C样式转换为不兼容的指针类型进行转换是未定义的行为 - 实际上任何事情都可能发生。

在这种情况下,编译器显然决定只信任你,并决定相信hi()确实是指向b实例的有效指针 - 这实际上都是C -style cast将会执行,因为它们不涉及运行时行为。当您在其上调用B时,该方法可以正常运行,因为:

  • 它不访问属于hi()但不访问B的任何实例变量(实际上,它根本不访问任何实例变量)
  • 它不是虚拟的,所以不需要在A的vtable中查找

因此它可行,但在几乎所有非平凡的情况下,这样的格式错误的强制转换后跟方法调用会导致崩溃或内存损坏。而且你不能依赖这种行为 - undefined 并不意味着每次运行它都必须是相同的。编译器完全在其权利范围内,使用此代码插入随机数生成器,并在生成1时,启动原始 Doom 的完整副本。每当涉及未定义行为的任​​何事情似乎都有效时,请牢记这一点,因为明天可能无法正常工作,您需要像这样对待它。