std::unique_ptr通过绑定资源生命周期实现自动释放,禁止复制仅允许移动,推荐用std::make_unique创建以保证异常安全,避免与shared_ptr混用或滥用自定义删除器。

用 std::unique_ptr 替换裸指针,内存自动释放
只要你在堆上 new 了对象,又没在所有路径上配对 delete,就存在泄漏风险。std::unique_ptr 的核心价值不是“更高级”,而是把“谁负责释放”这个责任绑定到对象生命周期上——离开作用域自动调用 delete,不依赖人肉检查。
常见错误现象:std::unique_ptr 被复制(比如传值或赋值给另一个 std::unique_ptr),编译直接报错:use of deleted function 'std::unique_ptr<t>::unique_ptr(const std::unique_ptr<t>&)'</t></t>。这不是 bug,是设计使然:它本就不该被复制。
- 只允许移动(
std::move(ptr)),比如传参时写func(std::move(ptr)) - 初始化必须显式,不能隐式转换:
std::unique_ptr<int> p = new int(42);</int>❌;要写成std::unique_ptr<int> p(new int(42));</int>或更推荐的std::make_unique<int>(42)</int> - 获取原始指针用
p.get(),但别存起来长期用——p一析构,指针就悬空
std::make_unique 是唯一推荐的创建方式
手写 new + unique_ptr 构造器,容易在异常路径下泄漏。比如 func(unique_ptr<t>(new T), heavy_computation())</t>:如果 heavy_computation() 抛异常,new T 分配的内存就永远没机会交给 unique_ptr 管理。
std::make_unique 是原子操作,内部先分配再构造,异常安全。
立即学习“C++免费学习笔记(深入)”;
- 正确:
auto p = std::make_unique<:string>("hello");</:string> - 错误(且已弃用):
std::unique_ptr<:string> p(new std::string("hello"));</:string> - 数组特例:
auto arr = std::make_unique<int>(100);</int>,此时operator[]可用,析构用delete[]
和 std::shared_ptr 混用时,别让 unique_ptr 持有 shared_ptr 管理过的内存
一个对象被 std::shared_ptr 管理后,它的生命周期由引用计数决定。std::unique_ptr 如果也指向它(比如通过 get()),再析构时会二次释放,导致未定义行为。
典型误用场景:封装 C API 回调函数,回调里把 shared_ptr 对象地址传给 C 层,C 层又转给某个 C++ 类成员函数,该函数错误地用 unique_ptr 接收。
- 绝对不要:
std::unique_ptr<t> p(ptr_from_shared_ptr.get());</t> - 如果需要转移所有权,用
std::shared_ptr::release()?不行——shared_ptr根本没有release()方法 - 真要交出所有权,只能从源头改:一开始就不该用
shared_ptr,或改用weak_ptr配合裸指针观察
自定义删除器会让 unique_ptr 大小翻倍,慎用
默认情况下,std::unique_ptr<t></t> 和裸指针一样大(通常 8 字节)。但一旦加了自定义删除器(比如用 fclose 关文件),编译器要把删除器对象塞进 unique_ptr 实例里,大小可能变成 16 字节甚至更大——对高频小对象(如节点指针)有缓存和内存占用影响。
使用场景:管理非 new 出来的资源,比如 FILE*、OpenGL 句柄、Windows HANDLE。
- 轻量替代:优先考虑 lambda(能内联):
std::unique_ptr<file decltype f fclose></file> - 避免捕获:lambda 不能带状态(如闭包变量),否则无法作为模板参数;需状态时用函数对象类
- 别忘了类型要完整:自定义删除器类型是模板参数一部分,头文件里得能看见定义
实际项目里最常漏掉的,是函数返回 unique_ptr 后,在调用方没注意它是否为空就直接解引用——p->method() 前不检查 p 是否为 nullptr,崩溃只在运行时发生。










