基class指针绑定到已删除的派生对象调用虚方法有什么影响

What is the effect of calling a virtual method by a base class pointer bound to a derived object that has been deleted

接下来的问题是:

  1. p->test() 在 b 被销毁后不应该工作。但是,代码运行没有任何问题,动态绑定仍然有效;
  2. 定义A的析构函数后,动态绑定不再起作用。这背后的逻辑是什么?
#include <iostream>
using namespace std;
struct A {
    //~A() {} 
    virtual void test() {        cout << 0 << endl;    }
};

class B :public A {
    void test() {        cout << 1 << endl;    }
};

int main() {
    A* p;
    {
        B b;
        p = &b;
    }
    p->test(); // the method called will be different if the destructor of A is removed
}
  1. p->test() should not work after b is destroyed. However, the code is running without any issue, the dynamic binding still works;

它不“有效”。 p->test() 调用未定义的行为。

  1. when the destructor of A is defined, the dynamic binding doesnot work anymore. What is the logic behind it?

它背后没有任何逻辑,除了您正在使用的编译器的实现细节之外,因为 C++ 标准没有强制要求编译器应该如何处理具有未定义行为的代码。

有关未定义行为的更多详细信息,请参阅 https://en.cppreference.com/w/cpp/language/ub

编译器无法检测到所有未定义的行为,但可以检测到其中的一些。使用 gcc,您可以尝试 -fsanitize=address 以更清楚地查看问题:https://godbolt.org/z/qxTs4sxcW.

欢迎来到未定义行为的世界!对已删除对象的任何访问,包括对其调用方法都会调用未定义的行为。

这意味着语言不需要特定的行为,并且取决于编译器的内部结构,并且可能取决于计算机上任何明显无关的 thing 预期行为可能会发生任何事情(您经历过)导致立即崩溃或立即或稍后发生意外结果。

永远不要尝试测试 UB 操作的结果:它可以从一个编译更改为一个新的编译,即使具有相同的配置,甚至可以从一个 运行 另一个编译更改,如果它依赖于未初始化的内存。

更糟糕的是:如果编译器可以检测到一部分代码中的 UB,它可以优化该部分,因为该语言允许它假定程序永远不应该调用 UB。

TL/DR: 你在这里调用UB。不。只是不要。