文章目录

C++智能指针make_shared比直接new+shared_ptr好在哪

发布于 2026-04-24 23:28:22 · 浏览 13 次 · 评论 0 条

C++智能指针make_shared比直接new+shared_ptr好在哪

C++11 引入了智能指针来管理动态内存,std::shared_ptr 是最常用的一种。但在创建 std::shared_ptr 时,直接使用 new 和使用 std::make_shared 存在显著差异。以下是详细的对比分析与实操指南。


1. 分析内存分配次数

直接使用 new 配合 shared_ptr 构造函数,会导致内存分配两次。而使用 make_shared 仅分配一次。

理解 new + shared_ptr 的行为:

当执行 std::shared_ptr<T> p(new T) 时:

  1. 执行 new T:在堆上分配一块内存用于存放对象 T
  2. 执行 shared_ptr 构造函数:在堆上分配第二块内存(控制块),用于存放引用计数、删除器等管理信息。

理解 make_shared 的行为:

当执行 auto p = std::make_shared<T>() 时:

  1. 执行 make_shared:编译器申请一块连续的内存,同时容纳对象 T 和控制块。

使用 Mermaid 流程图对比两种方式的内存布局差异:

graph LR subgraph A["Method 1: new + shared_ptr"] direction TB A1["Memory Block 1: Object T"] A2["Memory Block 2: Control Block (Ref Count)"] end subgraph B["Method 2: make_shared"] direction TB B1["Single Memory Block\n[ Object T | Control Block ]"] end

结论:减少内存分配次数能降低 CPU 开销,并减少内存碎片,提高缓存命中率。


2. 检查异常安全性

在函数参数传递时,使用 new 可能会导致内存泄漏。

分析 潜在的泄漏场景:

假设有一个函数接收两个参数,一个是 shared_ptr,另一个是复杂的对象。

void process(std::shared_ptr<Resource> res, AnotherObject obj);

如果这样调用:

// 危险写法
process(std::shared_ptr<Resource>(new Resource()), createAnotherObject());

C++ 标准不限制参数的求值顺序。编译器可能按照以下步骤执行:

  1. 执行 new Resource()(对象创建成功)。
  2. 调用 createAnotherObject()(假设该函数抛出异常)。
  3. 异常抛出,程序跳转到异常处理,shared_ptr 构造函数尚未执行,导致第一步创建的 Resource 内存泄漏。

使用 make_shared 解决问题:

// 安全写法
process(std::make_shared<Resource>(), createAnotherObject());

由于 make_shared 是一个函数调用,资源创建和控制块构造在函数内部完成,要么全成,要么全不成。即使 createAnotherObject() 抛出异常,make_shared 内部已创建的智能指针会自动负责销毁资源。


3. 对比代码性能与可读性

通过表格直接查看两种方式在关键维度上的差异。

特性 new + shared_ptr make_shared 优势方
内存分配次数 2 次 1 次 make_shared
代码简洁度 需重复写类型名 使用 auto 自动推导 make_shared
异常安全 不安全(特定场景) 安全 make_shared
对象销毁时机 引用归零即销毁 引用归零且无 weak_ptr 时才释放 new + shared_ptr

优化 代码书写:

直接写 new 必须重复类型名,容易笔误且冗余:

std::shared_ptr<MyComplexClass> ptr(new MyComplexClass(arg1, arg2));

使用 make_shared 配合 auto

auto ptr = std::make_shared<MyComplexClass>(arg1, arg2);

4. 注意使用限制与特殊情况

尽管 make_shared 通常是首选,但在一种特殊情况下需要使用 new:当对象非常大,且使用了 std::weak_ptr 时。

理解 内存释放机制:

  • new 方式:当 shared_ptr 引用计数归零,对象内存立即释放。控制块内存需等到所有 weak_ptr 引用归零才释放。
  • make_shared 方式:对象和控制块在同一块内存。只有当 shared_ptrweak_ptr 引用都归零时,这块内存才会被释放。

场景:如果有一个占用 1GB 内存的对象,且有一个 weak_ptr 长期存活(例如用于缓存检查),即使用户不再使用该对象,这 1GB 内存也会一直占用,直到 weak_ptr 到期。

操作 解决步骤:

  1. 判断 对象内存占用是否极大。
  2. 检查 是否存在生命周期可能很长的 weak_ptr
  3. 如果满足上述两点,使用 new + shared_ptr 来确保大对象内存能及时释放。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文