基class指针绑定到已删除的派生对象调用虚方法有什么影响
What is the effect of calling a virtual method by a base class pointer bound to a derived object that has been deleted
接下来的问题是:
- p->test() 在 b 被销毁后不应该工作。但是,代码运行没有任何问题,动态绑定仍然有效;
- 定义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
}
- p->test() should not work after b is destroyed. However, the code is running without any issue, the dynamic binding still works;
它不“有效”。 p->test()
调用未定义的行为。
- 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。不。只是不要。
接下来的问题是:
- p->test() 在 b 被销毁后不应该工作。但是,代码运行没有任何问题,动态绑定仍然有效;
- 定义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
}
- p->test() should not work after b is destroyed. However, the code is running without any issue, the dynamic binding still works;
它不“有效”。 p->test()
调用未定义的行为。
- 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。不。只是不要。