内存管理
new and delete
用于堆上分配和释放内存,与C中malloc
和free
相似。
int* pt=new int;
int* pt1=new int[10];
delete pt;
delete[] pt1; //表明
MyClass* obj = new MyClass; // 调用构造函数(constructor)
delete obj; // 调用析构函数(destructor)并释放内存
栈内存管理机制
为什么后定义的对象先被析构?
1. 栈的「后进先出」(LIFO)特性
当对象在同一个作用域内定义时,它们的内存分配在栈上。栈的特点是后进先出:
- 构造顺序:
obj1
先构造,obj2
后构造。 - 析构顺序:
obj2
先析构,obj1
后析构。
这种反向对称的顺序确保了 依赖关系的安全性。例如,若 obj2
依赖 obj1
,则 obj1
必须在 obj2
之后析构,否则 obj2
析构时可能访问已销毁的 obj1
。
2. 设计原则:资源释放的安全性
C++ 的析构顺序规则(后定义的对象先析构)与以下关键需求密切相关: - 避免悬空指针/引用:若对象 A 依赖对象 B 的资源,应保证 B 的析构晚于 A。 - 资源释放顺序:某些资源(如文件句柄、锁、内存)需要按特定顺序释放。例如:
void writeToFile() {
File file("data.txt"); // 先定义:打开文件
Lock lock(file); // 后定义:锁定文件
// 操作文件...
}
// 析构顺序:lock → file
- 如果
file
先析构(关闭文件),lock
析构时可能尝试操作已关闭的文件句柄,导致未定义行为。 - 实际中,
Lock
应该依赖File
的有效性,因此File
必须后析构。
例外情况
1. 堆分配的对象(动态内存)
通过 new
创建的对象不受栈规则约束,其析构顺序由 delete
的调用顺序决定:
2. 全局/静态对象
全局对象和静态对象的构造/析构顺序由编译器决定,通常: - 构造顺序:同一编译单元中按定义顺序构造,不同编译单元顺序不确定。 - 析构顺序:与构造顺序相反(但跨编译单元时可能不可预测)。
总结
场景 | 析构顺序规则 | 设计目的 |
---|---|---|
局部对象(栈) | 后定义的对象先析构(LIFO) | 确保依赖关系的安全性 |
堆对象 | 由 delete 顺序决定 |
用户手动控制生命周期 |
全局/静态对象 | 同一编译单元中析构顺序与构造相反 | 跨编译单元时不可预测,需谨慎使用 |
这种设计使得 C++ 的内存管理和资源释放更安全高效,尤其在 RAII(资源获取即初始化)模式中至关重要。