该用 std::unique_ptr 而不是裸指针时:需动态分配、作用域结束自动释放、明确独占所有权;它避免漏删/重复删/悬空访问,零开销且异常安全,优于裸指针和 shared_ptr(除非真需共享)。

什么时候该用 std::unique_ptr 而不是裸指针
当你需要动态分配对象、又希望它在作用域结束时自动释放,且明确不允许多个所有者共享该资源时,std::unique_ptr 就是首选。裸指针无法表达所有权语义,容易漏删、重复删或悬空访问;而 std::unique_ptr 把生命周期绑定到栈上变量,析构即释放,零运行时开销。
常见误用场景包括:函数返回裸指针后手动 delete(易忘)、多个函数间传递裸指针并模糊谁负责释放(易冲突)。换成 std::unique_ptr 后,这些责任完全由类型系统约束。
- 构造时直接传入
new表达式:auto ptr = std::make_unique(推荐)或(42); std::unique_ptr(不推荐,异常安全风险)ptr(new int(42)); - 不能拷贝,只能移动:
auto ptr2 = std::move(ptr);—— 此后ptr为空,ptr2拥有资源 - 获取原始指针用
ptr.get(),但绝不应拿它去delete或长期保存
std::unique_ptr 和 std::shared_ptr 怎么选
核心区别在所有权模型:std::unique_ptr 是独占所有权,std::shared_ptr 是共享所有权(带引用计数)。多数情况下优先选 std::unique_ptr:更轻量、无原子操作开销、语义清晰。
只有当确实需要多个地方同时持有同一资源、且无法通过重构避免(比如观察者模式中多个回调持有同一配置对象),才考虑 std::shared_ptr。滥用 std::shared_ptr 容易导致循环引用或意外延长生命周期。
立即学习“C++免费学习笔记(深入)”;
- 想把
unique_ptr转成shared_ptr?可以:std::shared_ptr,但不可反向(会丢失所有权语义)sp(std::move(up)); - 容器里存对象首选值语义;若必须存动态对象,用
std::vector<:unique_ptr>>,而非std::vector - 函数参数接收所有权?用
std::unique_ptr值传递(表示“你交给我管”);只读访问?用T&或const T*,别用std::unique_ptr&
自定义删除器怎么写才不出错
默认删除器调用 delete,但遇到 C 风格资源(如 fopen/fclose、malloc/free)或数组(new[])就必须自定义删除器,否则行为未定义。
关键点:删除器类型是 unique_ptr 模板参数的一部分,影响类型兼容性;Lambda 若含捕获则不能作为模板非类型参数,所以通常用函数指针或无捕获 Lambda + decltype。
- 数组场景:
std::unique_ptrarr_ptr(new int[10], [](int* p) { delete[] p; }); - C 文件句柄:
auto file_deleter = [](FILE* f) { if (f) fclose(f); }; std::unique_ptrfp(fopen("log.txt", "w"), file_deleter); - 错误示范:
std::unique_ptr—— 类型不匹配,编译失败p(new int, free);
常见崩溃和泄漏信号怎么看
运行时报 double free or corruption 或 segmentation fault,大概率是裸指针和 unique_ptr 混用:比如把 unique_ptr.get() 存进全局 map,之后又让 unique_ptr 析构;或者对已移动过的 unique_ptr 调用 get() 或 reset() 导致空指针解引用。
内存泄漏往往出现在异常路径:如果构造 unique_ptr 前已有其他可能抛异常的操作,裸指针就危险;而 std::make_unique 是强异常安全的 —— 它先分配内存再构造对象,失败时自动清理。
- 检查
unique_ptr是否为空再使用:if (ptr) { *ptr = 10; } - 禁止把
get()结果赋给另一个裸指针并期望自己管理:int* raw = ptr.get(); delete raw;—— 这会二次释放 - 调试时可加断点在
~unique_ptr(),确认释放时机是否符合预期
真正难的不是写出 std::unique_ptr,而是识别哪些资源本就不该用动态分配 —— 比如小对象、短生命周期对象,优先用栈分配;智能指针是兜底手段,不是银弹。










