对象生命周期由存储期决定,始于构造完成、终于析构开始;栈对象依作用域自动管理,堆对象依赖智能指针,静态对象跨整个程序运行期,析构开始即标志生命周期终结。

对象生命周期不是“从 new 到 delete”这么简单,而是由存储期决定的构造完成时刻到析构开始时刻之间的一段确定区间。你写的每一行定义、每一次 new、每一个 static,都在悄悄决定这个区间的起点和终点。
栈对象的生命周期:作用域即契约
栈对象的构造和析构时机最明确:进入作用域时调用构造函数,离开作用域时(哪怕提前 return)立即调用析构函数。编译器不讲情面,也不受异常影响——即使构造函数中途抛异常,已成功构造的成员会按逆序析构;即使函数里 throw 了,栈展开过程也会保证所有已构造栈对象被析构。
-
std::lock_guard就是靠这个机制实现自动解锁:它在构造时加锁,析构时解锁,只要它活在作用域里,就绝不会漏解 - 不要在构造函数里做耗时或可能失败的操作,否则栈展开时析构顺序不可控,容易引发连锁崩溃
- 避免在 lambda 捕获列表中按值捕获栈对象,一旦 lambda 生命周期超出原作用域,就会产生悬垂引用
堆对象的生命周期:new/delete 不是起点和终点
堆对象的生命周期起点确实是 new 表达式返回前(构造函数执行完),但终点不是 delete 那一刻,而是 delete 触发析构函数开始执行的瞬间。内存释放(operator delete)发生在析构之后,中间有严格顺序。
- 裸指针管理堆对象极易出错:
delete忘写、重复delete、delete后继续访问,都会导致未定义行为 -
std::unique_ptr和std::shared_ptr的析构函数才是真正的“终点守门员”,它们在析构时自动触发delete,把生命周期绑定到智能指针变量的作用域上 - 不要混用
new和free,或malloc和delete,这会导致析构函数不被调用、内存布局错乱
静态对象的生命周期:main 之前开始,exit 之后结束
静态存储期对象(全局变量、命名空间作用域变量、static 局部变量、类静态成员)的生命周期横跨整个程序运行期。但初始化顺序在跨翻译单元时是未定义的,析构顺序则是初始化顺序的严格逆序。
立即学习“C++免费学习笔记(深入)”;
- 函数内
static局部变量是线程安全的(C++11 起),首次执行到定义语句时才初始化,适合做单例缓存 - 两个文件里的全局对象若互相依赖(A 的构造函数访问 B,B 的析构函数访问 A),就可能在某个对象已析构后仍被访问,造成崩溃
- 动态库卸载时,若主程序还持有该库中静态对象的指针,就会变成悬空指针——这种问题在 Linux 上尤其隐蔽
析构函数开始执行那一刻,对象就“死了”
这是最容易被忽略的边界。析构函数体刚执行第一行,对象的生命周期就结束了。此时,虚函数表可能已被破坏,基类子对象已开始析构,成员变量处于半销毁状态。
- 析构函数里不能调用虚函数(会被静态绑定到当前类版本),也不能再访问已析构的基类或成员
- 不要在析构函数里抛异常——C++ 标准禁止这样做,会导致
std::terminate - 如果对象被
std::shared_ptr管理,且存在weak_ptr,析构开始后weak_ptr::lock()一定返回空,这是唯一可靠的“是否已销毁”判断方式


















