C++中非static局部变量销毁时机的判定原理探究
C++非static局部变量销毁时机的场景分析
非static局部变量的基本销毁规则是线程离开其作用域时销毁,但不同执行场景下的具体时机存在差异,结合三个测试示例拆解背后的逻辑:
首先是用于测试的两个类:
#include <iostream> using namespace std; class A { public: A() { cout << "A created\n"; } A(const A&) { cout << "A copied\n"; } ~A() { cout << "A destroyed" << endl; } }; class B{ public: B() { cout << "B created\n"; } B(const B&) { cout << "B copied\n"; } ~B() { cout << "B destroyed\n"; } };
示例1:返回静态对象的引用
代码
B& f() { A a; static B b; return b; } int main() { B b = f(); }
输出
A created B created A destroyed B copied B destroyed B destroyed
逻辑拆解
static B b是静态局部变量,生命周期覆盖整个程序运行期,第一次初始化后就常驻内存;- 非static局部变量
a的作用域仅限f()内部,当f()执行到return时,只是返回了静态b的引用地址,此时线程已经离开a的作用域,a立即销毁; - 之后
main中的b通过拷贝构造从静态b复制得到,程序结束时再依次销毁main里的b和静态b。
示例2:按值返回局部对象
代码
B g() { A a; B b; return b; } int main() { B b = g(); }
输出
A created B created B copied B destroyed A destroyed B destroyed
逻辑拆解
C++标准强制要求:当局部对象作为按值返回的源对象时,其销毁必须延迟到返回值的构造完成之后。
- 底层实现上,编译器会在调用者(
main)的栈帧中提前为返回值分配空间; g()内部先构造a和b,接着执行拷贝构造,把局部b的内容复制到提前分配的返回值空间;- 拷贝完成后,才按声明逆序销毁
g()里的局部变量b和a; - C++17之后的prvalue规则是优化层面的保障(确保返回值优化RVO合法),但核心是标准对销毁时机的约束,避免拷贝已销毁的内存。
示例3:抛出局部对象
代码
void fun() { A a; B b; throw b; } int main() try { fun(); } catch (B cb) { }
输出
A created B created B copied B copied B destroyed A destroyed B destroyed B destroyed
逻辑拆解
抛出异常时,标准要求:作为抛出源的局部对象,必须在异常对象构造完成之后才能销毁。
- 抛出
b时,首先拷贝构造一个存储在异常专用存储区的临时对象; - 该临时对象再拷贝构造
catch块中的cb; - 两次拷贝完成后,触发栈展开,按局部变量声明的逆序销毁
fun()里的b和a; - 最后程序结束时销毁
cb和异常临时对象。
核心规则总结
- 普通非static局部变量:默认在**线程离开作用域(函数结束、跳转出代码块)**时销毁;
- 特殊场景约束:
- 局部对象作为按值返回的源对象:销毁延迟到返回值构造完成;
- 局部对象作为抛出异常的源对象:销毁延迟到异常对象及catch块变量构造完成;
- 静态局部变量:生命周期为程序全程,不受函数返回影响。
内容的提问来源于stack exchange,提问作者CPPL
相关产品推荐
相关产品推荐

