std::unique_ptr必须用std::make_unique初始化以避免内存泄漏;shared_ptr循环引用需用weak_ptr打破;禁止混用智能指针与裸指针进行双重管理或释放操作。

std::unique_ptr 怎么初始化才不会崩溃
直接用裸指针初始化 std::unique_ptr 是最常见崩点。比如写 std::unique_ptr<int> p(new int(42))</int> 看似没问题,但一旦构造函数抛异常(比如 int 的构造里 throw),new 出来的内存就泄露了——因为 unique_ptr 构造还没完成,析构器根本没接管。
正确做法只有一条:用 std::make_unique。
-
std::make_unique<int>(42)</int>—— 安全,分配和构造原子完成 - 别手动 new + 传入构造函数,尤其别对数组用
std::unique_ptr<int>(new int[10])</int>,应改用std::make_unique<int>(10)</int> - 如果必须从裸指针接手(比如 C API 返回的指针),用
std::unique_ptr<t>(raw_ptr)</t>后立刻检查raw_ptr != nullptr,否则空指针移交会调用 delete nullptr(虽标准允许,但某些自定义 deleter 会崩)
std::shared_ptr 循环引用怎么破
两个 std::shared_ptr 互相持有对方管理的对象,引用计数永远不降到 0,对象就永远不释放——典型场景是父子结构、观察者回调、lambda 捕获自身 shared_ptr。
核心解法不是“少用 shared_ptr”,而是明确谁该强引用、谁该弱引用。
立即学习“C++免费学习笔记(深入)”;
- 父持有子用
std::shared_ptr,子反向引用父必须用std::weak_ptr - 在 lambda 里捕获
shared_ptr自身?改成捕获weak_ptr,调用前用lock()转成临时shared_ptr,失败就说明对象已销毁 - 别用
shared_from_this()在构造函数里调用——此时控制块还没建好,行为未定义
智能指针和 raw pointer 混用时哪些操作绝对不能做
混用本身不违法,但几类操作一做就踩内存或 double-free。
- 把
std::unique_ptr.get()返回的裸指针再传给另一个智能指针构造(比如又塞进std::shared_ptr)——双重管理,必崩 - 对
get()出来的指针调用delete或free——智能指针还在管,释放后变成悬垂指针 - 把
std::shared_ptr的get()结果存成全局裸指针并长期使用——忘了它可能随时被释放,下次访问就是 UAF - 用
std::shared_ptr管理栈对象(比如int x; std::shared_ptr<int>(&x)</int>)——析构时 delete 栈地址,UB
move 语义下 unique_ptr 和 shared_ptr 行为差异
看起来都是“转移所有权”,但底层动作完全不同,影响性能和线程安全。
-
std::unique_ptrmove 是纯指针赋值+置空,零开销,无原子操作,线程间传递需自行同步 -
std::shared_ptrmove 会把控制块里的引用计数从源减 1、目标加 1,但 move 本身不修改引用计数(只转移控制块指针),所以比 copy 快,但仍涉及一次指针复制和原对象置空 - 别对
shared_ptr频繁 move 来“优化性能”——除非你在 hot path 且 profile 确认控制块访问是瓶颈,否则收益极小,还增加理解成本 - move 后原
unique_ptr变成空,访问get()返回nullptr;而shared_ptrmove 后也为空,但它的控制块可能还在被其他副本引用
智能指针不是银弹,真正难的从来不是语法,而是判断“谁该拥有所有权”“生命周期边界在哪”。很多 crash 其实发生在跨模块、跨线程、或和 C 风格 API 交互时——那里没有自动管理,只有你写的那行 reset() 或 release() 是否恰到好处。









