std::unique_ptr的reset()执行顺序设计原因及相关疑问
关于std::unique_ptr::reset执行顺序的问题解答
1. 为何采用「存旧指针→替换当前托管指针→删除旧指针」的执行顺序
核心是为了规避三类未定义行为(UB):
- 首先是标准明确提到的场景:调用删除器时可能销毁当前
unique_ptr实例本身。如果执行顺序是「先删除旧指针→再替换当前托管指针」,删除操作销毁实例后,再对已销毁实例的current_ptr成员赋值属于野内存写入,直接触发UB。现有顺序把替换指针的操作放在删除之前,就算删除时实例被销毁,也不会有后续的实例成员写入操作,完全规避了该问题。 - 其次是避免入参指针提前失效:如果传入
reset的ptr和当前托管的指针指向重叠内存,或是ptr的有效性依赖当前托管对象的存续,先删除旧指针会直接导致ptr变成野指针,后续再赋值给current_ptr就会托管无效地址。 - 最后是保证删除器执行时的实例状态正确:自定义删除器内部允许调用
unique_ptr的get()等接口获取当前托管指针,现有顺序下执行删除器时,current_ptr已经被更新为新的入参指针,删除器读取到的是实例更新后的正确状态。
2. 「删除器可能销毁*this」是不是唯一原因
不是。除了这个原因外,上面提到的「避免入参指针提前失效」、「保证删除器执行时的实例状态正确」都是标准指定该执行顺序的核心考量因素。
3. 删除器调用销毁当前unique_ptr实例的场景
当自定义删除器持有当前unique_ptr实例的引用/指针,且删除器的执行逻辑包含销毁该实例的操作时,就会触发这种情况,示例代码如下:
#include <memory> #include <iostream> struct SelfDestructDeleter { std::unique_ptr<int>* self; // 持有unique_ptr实例本身的指针 void operator()(int* p) { std::cout << "deleting ptr: " << p << std::endl; delete p; delete self; // 执行删除时直接销毁当前unique_ptr实例 } }; int main() { // 构造unique_ptr时把自身指针传给删除器 auto* ptr = new std::unique_ptr<int, SelfDestructDeleter>( new int(42), SelfDestructDeleter{ptr} ); ptr->reset(); // 调用reset时会触发删除器销毁ptr指向的unique_ptr实例 return 0; }
内容的提问来源于stack exchange,提问作者Ruperrrt
相关产品推荐
相关产品推荐

