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

为何std::shared_ptr指向的指针会因前置步骤抛异常而销毁?

问题原因分析与解决方案

这个问题的核心在于临时std::shared_ptr对象的生命周期和异常抛出时的资源管理逻辑,我们一步步拆解清楚:

为什么指针会被销毁?

看你代码里的try块逻辑:

this->getList(key).addNew(std::shared_ptr<T>(ptr));

这里的std::shared_ptr<T>(ptr)是一个临时对象——它没有被赋值给任何命名变量,只是作为参数临时传递给addNew函数。

当getList(key)抛出std::out_of_range异常时,addNew函数根本不会被调用。此时,这个临时的shared_ptr对象会立刻被销毁(因为它的生命周期只存在于当前表达式中,异常会直接中断当前语句的执行流程)。而shared_ptr的析构函数会检查引用计数,此时引用计数是1(没有其他shared_ptr持有这个指针的所有权),所以它会自动调用delete释放ptr指向的内存,这就是你观察到指针被销毁的根本原因。

修复方案

方案1:提前创建命名的shared_ptr对象

把shared_ptr先创建为栈上的命名变量,这样即使异常抛出,它的生命周期也会延续到try-catch块结束,而且你可以在catch块中直接复用它:

void addTo(const Key &key, T* ptr) {
    if (!ptr) {
        throw std::invalid_argument("Pointer cannot be null"); // 推荐使用标准异常类型,而非字符串字面量
    }
    std::shared_ptr<T> sp(ptr); // 提前创建,持有ptr的所有权
    try {
        this->getList(key).addNew(sp);
    } catch (const std::out_of_range& ex) { // 用const引用捕获异常,避免不必要的拷贝开销
        this->addList(key, sp);
    }
    // 这里sp才会销毁,此时它的引用计数已经被addNew/addList增加,不会触发内存释放
}

方案2:用find替代异常捕获(更高效)

异常的设计初衷是处理意外错误场景,而非常规的流程控制。你可以直接用unordered_map的find方法检查key是否存在,完全绕开异常逻辑:

void addTo(const Key &key, T* ptr) {
    if (!ptr) {
        throw std::invalid_argument("Pointer cannot be null");
    }
    auto it = your_map.find(key); // 替换成你实际的unordered_map成员变量名
    std::shared_ptr<T> sp(ptr);
    if (it != your_map.end()) {
        it->second.addNew(sp);
    } else {
        this->addList(key, sp);
    }
}

额外建议

  • 避免抛出字符串字面量作为异常,尽量使用C++标准库提供的异常类型(比如std::invalid_argument、std::runtime_error等),这样调用者可以更规范地捕获和处理异常。
  • 捕获异常时优先使用const引用,避免异常对象的拷贝开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:31:58