为何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
相关产品推荐
相关产品推荐

