You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 22:45:34