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

为什么std::unique_ptr禁止访问正在销毁的对象?其设计逻辑是什么?

问题分析

两段代码的行为差异完全来自原始指针和std::unique_ptr释放资源的顺序不同:

  • 原始指针版本:delete p_a的执行顺序是先调用A的析构函数,再释放对应内存。整个析构函数执行过程中,p_a存储的地址仍然指向有效未销毁的A对象,因此lambda捕获的指针解引用合法,代码可以正常运行。
  • std::unique_ptr版本:p_a = {}等价于调用p_a.reset(),其内部执行顺序为:
    1. 将内部存储的原始指针保存到临时变量tmp
    2. 将自身内部的原始指针置为nullptr
    3. 调用删除器销毁tmp指向的A对象
      执行A的析构函数调用func时,外部的p_a已经是空智能指针,lambda捕获的是p_a的引用,调用p_a->value等价于对空智能指针解引用,直接触发未定义行为,表现为段错误。
std::unique_ptr该设计的核心逻辑

这个设计完全是为了满足强异常安全保证:
如果流程反过来,先销毁对象再修改内部指针:万一销毁对象时(自定义删除器或析构函数)抛出异常,std::unique_ptr内部仍然存储着已经被销毁的对象的地址,形成悬垂指针,后续所有对该std::unique_ptr的访问都会触发未定义行为,且std::unique_ptr的状态完全不可控。
而按现有流程执行的话,就算删除步骤抛出异常,std::unique_ptr的内部状态已经完成更新(已经置空或者指向新的接管对象),状态是确定且合法的,不会留下悬垂指针的隐患,符合C++标准库对异常安全的设计准则。你补充的期望完全符合这个设计的初衷:如果删除器抛出异常,std::unique_ptr不会持有已经失效的旧对象指针,避免了后续更隐蔽的内存错误。

代码修复方案

你可以修改lambda捕获的内容,直接捕获原始指针而非std::unique_ptr的引用,就能让第二段代码正常运行:

p_a->func = [ptr = p_a.get()]() { std::cout << ptr->value; };

内容的提问来源于stack exchange,提问作者Dmitriano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 11:39:03