C++程序什么情况下不会调用析构函数?

3
所以这是代码:

#include <iostream>

using namespace std;

class C {
public:
    C() {i = 6; cout << "A:" << i << endl;}

    C(int i0) {i = i0; cout << "B:" << i << endl;}

    ~C() {cout << "C:" << i << endl;}
private:
    int i;
};

int main(int argc, char* argv[]) {
    cout << "X" << endl;
    C *c = new C;
    cout << "Y" << endl;
}

由于某些原因,该代码的输出结果为:
X
A:6
Y

不知何故,当您到达代码结尾时,析构函数(C:6)从未被调用。这是为什么呢?同时,此代码确实调用了析构函数:

#include <iostream>

using namespace std;

class C {
public:
    C() {i = 0; cout << "A:" << i << endl;}

    C(int i0) {i = i0; cout << "B:" << i << endl;}

    ~C() {cout << "C:" << i << endl;}
private:
    int i;
};

int main(int argc, char* argv[]) {
    cout << "X" << endl;
    C c;
    cout << "Y" << endl;
}

通常情况下,你必须为每个使用new关键字创建的对象调用delete方法(注意:并非总是如此,但对于大多数情况都适用)。 - Kevin W.
C++不是C#,避免使用new - user2249683
1
什么时候不调用析构函数?永远不会。 :) - erip
@erip 不,如果您在不使用delete的情况下使用new,那么就不需要调用它 - 虽然这是个相当糟糕的想法。 - Anedar
1
好的,你无法确定析构函数是否被调用,因为“c”是“C”的指针类型,普通指针没有析构函数。在第二段代码中,“c”是“C”类型,而“C”确实有一个析构函数。因此,在一种情况下,对象是具有析构函数的类型,在另一种情况下,它是没有析构函数的类型。没有什么神秘的。 - David Schwartz
显示剩余2条评论
3个回答

11
因为你忘记写了

delete c;

如果您在程序中不删除使用 new 实例化的变量,将会导致内存泄漏。

编辑,因为您编辑了您的问题:

如果您编写类似于以下内容的代码:

C c;
C c{1};
C c = C{1};

你创建一个自动存储期变量。一旦声明该变量的函数退出(或更准确地说:声明该变量的块退出),变量将超出范围。在这种情况下,构造函数会自动调用。

如果您编写

C* c = new C{};

你创建了一个指向一个新的C的指针。指针本身具有自动存储期,这意味着c也将在其作用域结束时销毁。但是指针只保存类型为C的对象的地址。只有在调用delete c;时,才会删除此对象。如果您不调用delete,则您的程序会“遗忘”该对象的地址,但它不会释放内存或销毁对象。这就是内存泄漏。
但是一旦您的程序结束,所有内存都会被释放(不调用析构函数),因此在您的简单示例中,您不会注意到。


但是还有另一个代码(我在顶部发布的那个)我没有调用delete c,而是调用了析构函数。 - David Rolfe
我稍微扩展了一下这个内容。 - Anedar

5

很少有必要直接调用析构函数。

当一个对象被销毁时,无论是通过作为栈实例超出范围还是通过作为堆实例delete,都会自动调用析构函数。因此,如果您的析构函数未被调用,那么说明该对象已经丢失或泄漏。

new从堆内存中创建对象的新实例,并打开合同,当不再需要该对象时,您将负责调用delete将其返回到堆中(如果分配了数组,则使用delete [])。

在您的代码中,您从未delete所创建的实例。

#include <iostream>

using namespace std;

class C {
public:
    C() : m_i(6) { cout << "A:" << m_i << endl;}

    C(int i_) : m_i(i_) { cout << "B:" << m_i << endl;}

    ~C() {cout << "C:" << m_i << endl;}
private:
    int m_i;
};

int main(int argc, char* argv[]) {
    cout << "X\n";
    C* c = new C;
    C stackC;
    cout << "Y\n";
    delete c;
    cout << "Z\n";
}

实时演示: http://ideone.com/iuZim4

0
当您使用“new”来分配动态变量时,对象实例的生命周期将在以下情况下结束:1)删除或2)进程终止,或3)系统重置。
在您的代码中,在调用“delete c;”之前程序终止,因此析构函数未被进程调用。
进程使用的任何动态内存都会在操作系统终止进程时“回收”。这种操作系统的“回收”也不会调用析构函数。
当系统关闭或重新启动时,软件停止运行,因此也不会调用析构函数。
在嵌入式系统中,分配没有析构函数调用的对象是很常见的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接