我对以下C++代码感到困惑(可在http://cpp.sh/8bmp上在线运行)。它结合了我在类(class)中正在学习的几个概念。
#include <iostream>
using namespace std;
class A {
public:
A() {cout << "A ctor" << endl;}
virtual ~A() {cout << "A dtor" << endl;}
};
class B: public A {
public:
B() {cout << "B ctor" << endl;}
~B() {cout << "B dtor" << endl;}
void foo(){cout << "foo" << endl;}
};
int main(){
B *b = new B[1];
b->~B();
b->foo();
delete b;
return 0;
}
输出:
A ctor
B ctor
B dtor
A dtor
foo
A dtor
这是我不明白的:
foo
? delete
? delete b;
,此代码会泄漏内存吗? A
的析构函数是虚拟的。我认为不会调用子类中重载的虚函数。为什么~A()
会被调用? b->~B();
,则在B dtor
之后打印foo
行。为什么? b->~B();
行两次,则输出为:B dtor\nA dtor\nA dtor
。 ?? delete B;
切换delete[] b;
,我将得到相同的输出。我认为第二个是正确的,因为b
是使用new[]
创建的,但这没关系,因为我只是将B
的一个实例推入堆中。那是对的吗? 很抱歉问了这么多问题,但这令我感到困惑。如果我的个人问题被误导了,请告诉我了解每个析构函数何时运行所需的知识。
最佳答案
“未定义的行为”(简称UB)是允许编译器执行任何操作的位置-这通常意味着介于“崩溃”,“给出错误的结果”和“仍然执行您期望的操作”之间。您的b->foo()
绝对是未定义的,因为它发生在b->~B()
调用之后,
由于您的foo
函数实际上并没有使用析构函数销毁的任何东西,因此对foo
的调用“有效”,因为没有使用任何已销毁的东西。 [这是绝对不能保证的-它只是工作而已,有点像有时候跨过马路不看就好,有时则不然。视其路况而定,这可能不是一个好主意,或者在大多数情况下可能会奏效-但是有一个原因,人们说“向左看,向右看,向左看,然后在安全的情况下越过”(或类似的意思)那)]
在已被破坏的对象上调用delete
也是UB,所以再次幸运的是,它“有效”(在“不会导致程序崩溃”的意义上)。
UB-将delete
与new []
混合使用,或者反之亦然-UB –再次,编译器[及其相关的运行时]可能会根据情况和条件做对或错事。
不要依赖程序中未定义的行为[1]。它一定会回来咬你。 C和C++有很多UB案例,因此最好至少了解一些最常见的案例,例如“销毁后使用”,“销毁后使用”等,并留意此类情况-并避免不惜一切代价!
关于c++ - 我如何理解这些析构函数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34258299/