shared_ptr构造传nullptr不会崩溃,但解引用会触发未定义行为;reset()释放旧资源且不影响其他共享者,而=赋值更安全且原子;make_shared更高效因单次分配;循环引用需用weak_ptr破。

shared_ptr 构造时传 nullptr 会崩溃吗
不会崩溃,但容易误以为“安全”而忽略后续解引用风险。std::shared_ptr<int> p(nullptr)</int> 是合法的,p 确实持有空指针、引用计数为 1,但一旦写 *p 或 p->xxx 就触发未定义行为(通常是段错误)。
- 常见错误现象:
shared_ptr从工厂函数返回前被意外赋值为nullptr,调用方没检查就直接解引用 - 正确做法:构造后用
if (p)或p != nullptr显式判空,别依赖隐式转换 - 注意:
shared_ptr<t>()</t>和shared_ptr<t>(nullptr)</t>效果一致,但后者语义更明确
reset() 和赋值 = 的区别在哪
关键在是否释放旧资源、是否影响其他共享该对象的 shared_ptr 实例。
-
p.reset(new int(42)):先释放p当前持有的资源(如果非空),再接管新对象;其他同享该对象的shared_ptr不受影响 -
p = std::make_shared<int>(42)</int>:同样会释放旧资源,但更安全(避免裸指针泄漏);且=是原子操作,适合多线程场景下替换整个智能指针 - 坑点:
p.reset()(无参)等价于p.reset(nullptr),会清空指针但保留控制块——内存没全释放,可能造成轻微浪费
为什么 make_shared 比 shared_ptr(new T) 更高效
因为一次内存分配搞定控制块 + 对象本体,而 shared_ptr(new T) 至少要两次分配(new 一次,控制块再 new 一次)。
- 性能影响:小对象差异不明显,但高频创建时分配次数减半,缓存局部性更好
- 兼容性限制:
make_shared要求T的构造函数可访问(不能是私有或 explicit 构造函数被隐式调用) - 例外场景:需要自定义删除器(deleter)或别名构造(aliasing constructor)时,只能用
shared_ptr构造函数,不能用make_shared
循环引用导致内存泄漏怎么破
两个 shared_ptr 相互持有对方所管对象,引用计数永远不归零,析构不触发。
立即学习“C++免费学习笔记(深入)”;
- 典型场景:父子结构(如树节点)、观察者模式中回调捕获自身
shared_ptr - 解法:把其中一端改成
std::weak_ptr,访问前用lock()升级成临时shared_ptr,失败说明对象已销毁 - 容易踩的坑:在 lambda 中捕获
shared_ptr后又把它赋给成员变量,形成隐式循环;改用[weak = weak_from_this()]捕获更安全
shared_ptr 不是万能胶水,它只解决“谁负责释放”的问题,不自动解决“谁该持有”和“何时持有”的逻辑问题。最常出错的地方,恰恰藏在看似最省事的写法里——比如直接 = 赋值、不检查空、或者在闭包里随手捕获 this。










