1. 为什么C++需要make_shared?
第一次看到make_shared时,我也纳闷——直接用shared_ptr的构造函数不香吗?直到在线上环境遇到一次内存泄漏事故后,我才真正理解它的价值。简单来说,make_shared不是语法糖,而是解决shared_ptr底层设计痛点的工程方案。
1.1 shared_ptr的内存布局痛点
传统shared_ptr<Object> p(new Object)的构造方式实际上隐藏着一个严重问题。当这行代码执行时:
- 首先在堆上分配Object对象本身的内存
- 然后在另一个堆区域分配控制块(control block),用于存储引用计数等元数据
这就导致对象实例和控制块在内存中是分离的,就像把一个人的身体和身份证分别存放在城市两端。这种分离会带来三个实际问题:
- 内存碎片化:两次独立分配可能产生内存间隙
- 访问开销:访问对象时需要间接跳转
- 异常安全问题:如果在第一步和第二步之间抛出异常,会导致内存泄漏
1.2 make_shared的解决方案
make_shared的核心创新在于合并分配:
cpp复制auto p = std::make_shared<Object>();
这个调用会:
- 一次性分配连续内存块,同时容纳Object实例和控制块
- 在原地构造Object对象
- 初始化控制块信息
这种设计就像给对象分配了一个带身份证插槽的公寓,身体和证件永远在一起。实测在Linux x86_64环境下,这种优化可以减少约30%的内存分配开销。
关键点:合并分配不仅是内存优化,更是异常安全保证。因为内存分配和对象构造在一个原子操作中完成,不存在中间状态。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. make_shared的五大核心优势
2.1 性能优势:一次分配 vs 两次分配
通过基准测试可以清晰看到差异。以下测试代码在i9-13900K上运行(单位:纳秒):
| 操作方式 | 平均耗时 | 内存分配次数 |
|---|---|---|
| shared_ptr构造函数 | 142 | 2 |
