C++怎么用智能指针 C++中make_shared性能优势【重点】

1次阅读

C++怎么用智能指针 C++中make_shared性能优势【重点】

为什么 make_sharedshared_ptr 构造更高效

因为 make_shared 一次内存分配就能搞定控制块和对象,而直接用 shared_ptr 构造(比如 shared_ptr<t>(new T)</t>)要分配两次:一次给对象,一次给控制块。

控制块里存引用计数、弱引用计数、销毁器等元数据,它必须和对象生命周期解耦——但不意味着非得分开分配。

  • make_shared<int>(42)</int>:底层调用 operator new(sizeof(control_block) + sizeof(int)),连续布局
  • shared_ptr<int>(new int(42))</int>:先 new int,再单独为控制块分配内存,缓存局部性差,还多一次系统调用开销
  • 对小对象(如 intstd::String 等内部有 small-string 优化的类型),性能差异明显;对大对象,分配开销占比下降,但构造顺序和异常安全优势仍在

make_shared 不能用于哪些场景

它要求类型必须可公开构造,且不能是 private/protected 构造函数,也不能是 explicit 构造函数(c++17 起放宽了部分限制,但仍有坑)。

常见踩坑点:

立即学习C++免费学习笔记(深入)”;

  • 类只有 explicit 单参构造函数时,make_shared<t>(arg)</t> 在 C++17 前会编译失败;C++17 后允许,但语义仍是“隐式转换+构造”,需确认是否符合预期
  • 类构造函数是 privateprotected,且没把 std::make_shared 声明为友元——此时只能退回到 shared_ptr<t>(new T(...))</t> 或自定义 factory 函数
  • 需要自定义删除器(deleter)或分配器(allocator):make_shared 不接受这些参数,必须用 shared_ptr 构造函数
  • 想用 placement-new 或共享已有内存(比如对象已存在于某 buffer 中):make_shared 总是自己分配,不支持

make_shared 时参数转发的陷阱

它用的是完美转发(std::forward),所以实参的值类别(左值/右值)会被保留。这在涉及移动语义或临时对象时容易出问题。

  • 传入一个具名的 std::string 变量:make_shared<a>(s)</a> → 会拷贝 s,不是移动;想移动得写 make_shared<a>(std::move(s))</a>
  • 传入字面量字符串make_shared<:string>("hello")</:string> → 构造临时 std::string,再移动进目标对象,没问题
  • 若类 A 的构造函数接受 const std::string&,而你传了右值,仍会触发拷贝(因 const 引用可绑定右值但不触发移动);这时不如显式用 std::move 或改接口
  • 多个参数时,每个都按原样转发,没有“自动降级”;比如 make_shared<foo>(std::move(a), b, std::forward<c>(c))</c></foo> 是合法且推荐的写法

兼容性与实际建议:什么时候别硬上 make_shared

不是所有 shared_ptr 场景都适合 make_shared,尤其当代码要跑在老标准或受限环境里。

  • C++11 编译器支持 make_shared,但早期 MSVC 2013 对某些模板推导有 bug;GCC 4.8+、Clang 3.3+ 基本稳
  • 如果对象构造可能抛异常,make_shared 的异常安全更好(控制块和对象要么都成,要么都不成);但若你已在用 new + shared_ptr 手动管理,且逻辑稳定,没必要强行改
  • 调试时发现 shared_ptruse_count() 行为异常?检查是否混用了 make_shared 和裸指针构造——它们的控制块实现不同,但标准保证互操作;不过跨 DLL 边界时,若 ABI 不一致(比如不同 STL 实现),可能崩溃
  • 真正该优先选 make_shared 的,是新写的、构造简单、无特殊访问控制、不需要自定义 deleter 的代码;其余情况,宁可明确写 shared_ptr<t>(new T(...))</t>封装 factory,也别为了“看起来高级”硬套

控制块内存布局、异常路径下的资源释放、跨模块的 ABI 兼容——这些地方一旦出问题,很难只靠加个 make_shared 解决。

text=ZqhQzanResources