sud*_*ash 1 c++ oop heap destructor virtual-destructor
我对以下C++代码感到困惑(在http://cpp.sh/8bmp上在线运行).它结合了我在课程中学到的几个概念.
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
A ctor
B ctor
B dtor
A dtor
foo
A dtor
Run Code Online (Sandbox Code Playgroud)
这是我不明白的:
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,所以再次,它是"运作"的纯粹运气(在"不会导致程序崩溃"的意义上).
delete与new []UB 混合或反之亦然 - 再次,编译器[及其相关的运行时]可能会做正确或错误的事情,具体取决于环境和条件.
不要依赖程序中未定义的行为[1].它肯定会回来咬你.C和C++有相当多的UB案例,至少可以理解最常见的案例,例如"破坏后使用","免费使用"等等,并注意这些案例 - 并避免它不惜一切代价!