必须用 std::shared_ptr(new T) 的场景包括:需自定义删除器、类构造函数私有/受保护且 make_shared 非友元、需对象构造后立即调用额外初始化方法、C++17 前使用数组类型、构造函数 explicit 或重载导致模板推导失败、类禁用 operator new、或需避免大对象与控制块内存强耦合以利内存及时回收。

优先用 std::make_shared,除非你明确需要它不支持的特性——比如自定义删除器、私有构造函数调用、或对象析构后仍需保留控制块(典型如长期存活的 weak_ptr)。
什么时候必须用 shared_ptr(new T)?
不是“偏好”,而是硬性限制:某些场景下 std::make_shared 根本无法编译或行为不符合预期。
- 需要自定义删除器:例如管理文件描述符、C API 资源,必须写
std::shared_ptr<foo>(new Foo(), [](Foo* p) { close(p->fd); })</foo>;make_shared不接受删除器参数 - 类构造函数是 private/protected 且
make_shared不是友元:它在类外调用构造函数,无权访问受限成员 - 要立即获取原始指针做额外初始化(如调用
init()方法),而该方法不能放进构造函数里:因为make_shared的对象构造和控制块分配是一体的,无法中途介入 - C++17 之前使用数组类型(如
int[10]):旧标准不支持make_shared<int>()</int>,只能shared_ptr<int>(new int[10])</int>
make_shared 看似方便,但构造失败时容易误判原因
报错 error: no matching function for call to 'make_shared' 很常见,但它不等于“构造函数写错了”——更可能是转发规则卡住了。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 传入
nullptr且目标构造函数不接受裸指针:编译器可能推导失败,改用static_cast<t>(nullptr)</t>或显式模板参数 - 构造函数是
explicit:比如explicit Widget(int),那么make_shared<widget>(5.0)</widget>会失败(隐式转换被禁),必须传5或int(5.0) - 多个重载构造函数参数类型相近(如
Foo(int)和Foo(long)):字面量5可能引发模板推导歧义,显式构造再转成shared_ptr更稳 - 类禁用了
operator new(如自定义内存池但未适配make_shared):它内部依赖全局或类作用域的operator new,否则编译不过
大对象 + 长期 weak_ptr 时,make_shared 可能拖慢内存回收
这不是性能问题,而是内存驻留问题:当所有 shared_ptr 已销毁,只剩 weak_ptr 活着时,make_shared 分配的整块内存(对象 + 控制块)都无法释放。
立即学习“C++免费学习笔记(深入)”;
- 对象很大(如百 MB 缓冲区)、且存在全局
weak_ptr观察它:对象已析构,但内存壳子仍占着 OS 堆,直到weak_ptr销毁 - 用
shared_ptr(new T)则不同:对象内存可立即归还 OS,只剩几字节控制块挂着 - 这种差异在服务长期运行、内存敏感场景(如嵌入式、实时系统)中会被放大,不能只看分配快慢
真正难决策的点不在语法或效率,而在于你是否清楚控制块生命周期与对象内存的耦合关系——make_shared 把它们焊死了,shared_ptr(new T) 让它们松散绑定。选哪个,取决于你愿不愿意为这点解耦,多写几个字符、多担一点异常安全风险。

















