make_shared更推荐是因为它一次分配内存(对象+控制块),而shared_ptr(new T)需两次分配,性能更高且异常安全:若T构造抛异常,make_shared能确保无内存泄漏。

make_shared 为什么比 new + shared_ptr 构造更推荐
因为 make_shared 在一次内存分配中同时构造控制块和对象,而 shared_ptr<t>(new T(args))</t> 至少要分配两次内存(一次给对象,一次给控制块),性能更好,且异常安全:如果 T 构造抛异常,make_shared 能确保控制块和对象都未泄漏。
常见错误是误以为 make_shared 支持私有构造函数或 explicit 构造函数——它不绕过访问控制,也不隐式转换;若类有 explicit 构造函数,传参必须显式匹配。
-
make_shared要求类型可移动或可复制(用于内部转发),不支持仅含 delete 拷贝/移动构造的类型 - 不能用于需要自定义删除器的场景(比如文件句柄、C API 资源),此时必须用
shared_ptr的原始构造函数 - 数组类型(如
T[])不能直接用make_shared(C++17 前不支持,C++17 起支持但需配合shared_ptr<t></t>)
基本用法与参数转发细节
make_shared 本质是完美转发参数给 T 的构造函数,所以引用、右值、初始化列表都按预期工作。注意:它不接受初始化列表语法({...})直接作为参数,除非目标构造函数明确接受 std::initializer_list。
例如:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
auto p1 = std::make_shared<std::vector<int>>(3, 42); // 调用 vector(size_t, T)
auto p2 = std::make_shared<std::vector<int>>(std::initializer_list<int>{1,2,3}); // OK,显式类型
- 传入左值时会被拷贝(除非构造函数接受 const T& 并做了优化)
- 传入临时对象(如
std::string("hello"))会移动(如果移动构造可用) - 若构造函数是
explicit,传参也必须满足显式要求,比如explicit Foo(int)→make_shared<Foo>(5)合法,但make_shared<Foo>(5.0)编译失败
常见编译错误及定位方法
最典型的错误是 error: no matching function for call to 'make_shared',通常源于三类问题:
- 构造函数不可访问(private/protected 且非友元)→
make_shared不在类内部,无法调用 private 构造 - 参数类型不匹配(隐式转换被禁、缺少 const& 或 && 重载)→ 查看错误信息里列出的候选函数,比对实际传参
- 模板推导失败(如传入
nullptr且无对应构造函数)→ 改用static_cast<T*>(nullptr)或直接传nullptr(C++11 起支持)
另一个容易忽略的是:当类有多个构造函数且参数类型相近(比如 Foo(int) 和 Foo(long)),传 5 可能因整型提升规则导致歧义,编译器可能拒绝推导——此时显式构造再转 shared_ptr 反而更稳。
与自定义分配器或特殊内存布局的兼容性
make_shared 默认使用全局 operator new,不支持传入自定义分配器(如 std::allocator)。C++20 引入了 allocate_shared 来解决这个问题,用法类似:
auto p = std::allocate_shared<MyType>(my_alloc, arg1, arg2);
如果你正在用内存池或对齐敏感类型(比如 SIMD 结构体),make_shared 分配的内存不一定满足对齐要求(尽管多数实现会做合理对齐,但标准不保证)。此时应避免 make_shared,改用 shared_ptr 配合 aligned_alloc + 自定义删除器。
另外,make_shared 创建的对象和控制块紧邻存储,调试器有时会把它们当成一块内存显示,容易误判对象大小——实际对象大小仍是 sizeof(T),控制块额外占用空间不计入。

















