std::unique_ptr适用于独占所有权场景,移动后原指针为空,零开销;std::shared_ptr用于共享所有权,有引用计数开销和循环引用风险;std::weak_ptr仅观察对象存续,需lock()判空使用;禁用std::auto_ptr和裸new/delete。

std::unique_ptr 适合“独占所有权”的场景
它表示资源只能被一个指针持有,移动后原指针自动变空——这是最轻量、零开销的智能指针。用在需要明确生命周期边界的地方,比如函数返回新对象、容器里存动态对象、RAII 资源封装。
常见错误是试图拷贝 std::unique_ptr:编译器会直接报错 use of deleted function,这不是 bug,是设计使然。别硬加 .get() 再手动 new,那等于放弃智能指针的价值。
实操建议:
- 构造时优先用
std::make_unique<t>(...)</t>,避免裸new - 传参用
const std::unique_ptr<t>&</t>(只读)或按值移动(转移所有权) - 放进
std::vector没问题,但别用std::shared_ptr去接它的.get(),会 double-delete
std::shared_ptr 的引用计数不是免费的
它靠原子计数支持多所有者共享,但每次拷贝、赋值、析构都要操作计数器,还额外分配控制块(control block),内存和性能开销比 std::unique_ptr 明显更高。
立即学习“C++免费学习笔记(深入)”;
典型误用是“图省事全用 shared_ptr”:比如局部变量、短生命周期对象、不需要跨作用域共享的场合。结果是缓存不友好、分配碎片多、调试时发现对象死活不释放(循环引用)。
实操建议:
- 只在真正需要共享所有权时用,例如观察者模式里的回调持有、对象图中多入口访问
- 警惕循环引用:两个
std::shared_ptr互相持有对方,得用std::weak_ptr打断 - 创建时也优先用
std::make_shared<t>(...)</t>,它能把对象和控制块一次分配,比先new再包更高效
std::weak_ptr 不是“弱引用”,而是“非拥有型观察者”
它不增加引用计数,只用来观察 std::shared_ptr 管理的对象是否还活着。不能直接解引用,必须调用 .lock() 转成 std::shared_ptr 才能安全使用。
常见错误是把 std::weak_ptr 当成“可空的 shared_ptr”来用,或者在 .lock() 后不检查返回值就直接 *ptr——这时如果原对象已销毁,.lock() 返回空 std::shared_ptr,解引用会崩溃。
实操建议:
- 专用于打破循环引用:A 持有 B 的
std::shared_ptr,B 用std::weak_ptr回指 A - 缓存、监听器列表等需“临时借用”对象的场景,避免延长生命周期
- 每次
.lock()后必须判空:if (auto p = wp.lock()) { /* use *p */ }
不要用 std::auto_ptr(已移除)、也不要裸 new + delete
std::auto_ptr 在 C++11 就被删了,它的拷贝语义是“转移”,极易引发静默悬空指针。现在所有主流标准库(GCC、Clang、MSVC)都不再支持它,代码里出现就是编译错误。
裸 new/delete 最大的坑不是写错,而是异常安全:构造中途抛异常,delete 就永远不会执行。哪怕你写了 try/catch,也难覆盖所有路径。
实操建议:
- 看到
std::auto_ptr,立刻换成std::unique_ptr,接口基本一致,语义清晰 - 任何需要动态分配的地方,先想“谁负责释放”,再选对应智能指针类型;没想清楚就先卡住
- 第三方 API 要求裸指针?用
.get()提供地址,但别交出所有权;要交出,用.release()并确保对方真会delete
所有权模型看着简单,实际踩坑多在边界模糊处:比如 lambda 捕获智能指针时用值还是引用、容器里放指针还是对象、跨线程传递要不要 std::shared_ptr。这些地方没有银弹,得看数据流和生命周期图。










