c++什么时候程序不调用析构函数?
When does the program not call the destructor in c++?
代码如下:
#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;
}
因为你忘了写
delete c;
如果你只是继续你的程序而不删除用 new 实例化的变量,你将导致内存泄漏。
编辑,因为您编辑了您的问题:
如果你这样写
C c;
C c{1};
C c = C{1};
您创建了一个具有自动存储持续时间的变量。一旦在退出中声明的函数(或更准确地说:一旦在退出中声明的块),它将 运行 超出范围。在这种情况下,构造函数被自动调用。
如果你写
C* c = new C{};
您创建了一个指向(新)C 的指针。指针本身具有自动存储持续时间,这意味着 c 也将 运行 超出范围。但是这个指针只保存了C类型对象的地址。而且这个对象只有在你调用delete c;
时才会被删除。如果你不调用 delete,你的程序 "forgets" 对象的地址,但它不会释放内存或销毁对象。那是内存泄漏。
然而,一旦您的程序结束,所有内存都将被释放(无需调用析构函数),因此在您的小示例中您不会注意到。
需要自己直接调用析构函数的情况非常
当一个对象被销毁时,析构函数会被自动调用,无论是超出堆栈实例的范围还是 delete
d 堆实例。所以你的析构函数没有被调用的事实告诉你一些事情:对象正在丢失或泄漏。
new
从堆内存中创建对象的新实例并打开一个合约,当不再需要该对象时,您将负责调用 delete
到 return 它到堆。 (或者 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";
}
当您使用 'new' 分配动态变量时,对象实例的生命周期以 1) 删除或 2) 进程终止或 3) 系统重置结束。
在您的代码中,程序在调用 'delete c;' 之前终止,因此进程不会调用 dtor。
当 OS 终止进程时,进程使用的任何动态内存都是 'reclaimed'。此 OS 'Reclaiming' 也不会调用 dtor。
并且当系统关闭或重新启动时,软件停止运行,因此不会调用dtor。
在没有析构函数调用的情况下分配对象在嵌入式系统中很常见。
代码如下:
#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;
}
因为你忘了写
delete c;
如果你只是继续你的程序而不删除用 new 实例化的变量,你将导致内存泄漏。
编辑,因为您编辑了您的问题:
如果你这样写
C c;
C c{1};
C c = C{1};
您创建了一个具有自动存储持续时间的变量。一旦在退出中声明的函数(或更准确地说:一旦在退出中声明的块),它将 运行 超出范围。在这种情况下,构造函数被自动调用。
如果你写
C* c = new C{};
您创建了一个指向(新)C 的指针。指针本身具有自动存储持续时间,这意味着 c 也将 运行 超出范围。但是这个指针只保存了C类型对象的地址。而且这个对象只有在你调用delete c;
时才会被删除。如果你不调用 delete,你的程序 "forgets" 对象的地址,但它不会释放内存或销毁对象。那是内存泄漏。
然而,一旦您的程序结束,所有内存都将被释放(无需调用析构函数),因此在您的小示例中您不会注意到。
需要自己直接调用析构函数的情况非常
当一个对象被销毁时,析构函数会被自动调用,无论是超出堆栈实例的范围还是 delete
d 堆实例。所以你的析构函数没有被调用的事实告诉你一些事情:对象正在丢失或泄漏。
new
从堆内存中创建对象的新实例并打开一个合约,当不再需要该对象时,您将负责调用 delete
到 return 它到堆。 (或者 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";
}
当您使用 'new' 分配动态变量时,对象实例的生命周期以 1) 删除或 2) 进程终止或 3) 系统重置结束。
在您的代码中,程序在调用 'delete c;' 之前终止,因此进程不会调用 dtor。
当 OS 终止进程时,进程使用的任何动态内存都是 'reclaimed'。此 OS 'Reclaiming' 也不会调用 dtor。
并且当系统关闭或重新启动时,软件停止运行,因此不会调用dtor。
在没有析构函数调用的情况下分配对象在嵌入式系统中很常见。