当前位置: 首页 > news >正文

C++为什么推荐使用 make_shared 而不是 new 构造 shared_ptr?

大家好,我是小康。

C++为什么推荐使用 make_shared 而不是 new 构造 shared_ptr?

看到这个问题,我想起了之前帮同事定位的一个线上bug。那是一个偶发的内存泄漏,最后追查发现就是因为不当使用shared_ptr(new T())导致的异常安全问题。当时如果用了make_shared,这个bug根本不会出现。

所以今天就系统地聊聊这个看似简单、实则暗藏玄机的话题。


简单说:性能更好、更安全、代码更简洁。

作为一个写了多年C++的老司机,我见过太多因为不理解make_sharednew的区别而踩坑的代码。 下面我会从原理到实践,把这个问题讲透。

一、核心区别:一次分配 vs 两次分配

先上结论:这是最重要的区别

使用 new 的方式:

std::shared_ptr<Widget>sp(newWidget());

这种方式至少需要两次内存分配:一次为 Widget 对象分配内存,另一次为 shared_ptr 的控制块(存储引用计数等信息)分配内存。

内存布局大概是这样的:

[Widget对象] <--- 第一次分配 ... [控制块(引用计数等)] <--- 第二次分配

使用 make_shared 的方式:

autosp=std::make_shared<Widget>();

make_shared 通常只执行一次内存分配,将对象和控制块放在连续的内存块中。

[控制块 + Widget对象] <--- 一次分配搞定

性能提升有多大?

  • 减少一次内存分配/释放操作
  • 减少内存碎片
  • 提升缓存局部性(控制块和对象在一起,CPU缓存命中率更高)

在高性能场景下,这个差异是相当可观的。如果你的程序频繁创建 shared_ptr,这个优化积累起来效果明显。

二、异常安全性:这个更致命

来看一个经典的坑:

voidprocessWidget(std::shared_ptr<Widget>sp,intpriority);// 调用方式1:使用 new (危险!)processWidget(std::shared_ptr<Widget>(newWidget()),computePriority());// 调用方式2:使用 make_shared (安全)processWidget(std::make_shared<Widget>(),computePriority());

问题出在哪?

在第一种方式中,如果 computePriority() 抛出异常,可能导致内存泄漏。

为什么?因为C++对函数参数的求值顺序是未定义的,可能的执行顺序:

  1. new Widget()- 分配内存
  2. computePriority()- 抛出异常!
  3. std::shared_ptr<Widget>(...)- 永远不会执行

结果:Widget 对象被分配了,但 shared_ptr 还没构造,内存泄漏!

而 make_shared 是异常安全的,因为它在单个操作中完成内存分配和 shared_ptr 构造。

三、代码简洁性

// 繁琐且容易出错std::shared_ptr<SomeVeryLongTypeName>sp1(newSomeVeryLongTypeName(arg1,arg2,arg3));// 简洁优雅autosp2=std::make_shared<SomeVeryLongTypeName>(arg1,arg2,arg3);

不需要重复类型名称,使用auto一步到位。代码可读性和维护性都更好。

四、make_shared 的局限性

当然,make_shared也不是万能的,有几个场景必须用new:

1. 需要自定义删除器

// make_shared 不支持自定义删除器autosp=std::shared_ptr<FILE>(fopen("file.txt","r"),&fclose);

2. 构造函数是私有的

make_shared 要求构造函数必须是公有的,因为它不是类的成员函数。

classSingleton{private:Singleton(){}public:staticstd::shared_ptr<Singleton>create(){// 这里不能用 make_sharedreturnstd::shared_ptr<Singleton>(newSingleton());}};

3. weak_ptr 生命周期问题

如果有 weak_ptr 长期持有引用,使用 make_shared 可能导致对象内存无法及时释放,因为对象和控制块在同一内存块中。

举个例子:

autosp=std::make_shared<HugeObject>();// 假设这个对象很大std::weak_ptr<HugeObject>wp=sp;sp.reset();// 对象被销毁,但...// 只要 wp 还活着,整个内存块(包括 HugeObject 的空间)都无法释放!

如果用new方式,对象内存和控制块分开,对象销毁后就能立即释放内存。

五、最佳实践总结

默认情况下,优先使用 make_shared:

autosp=std::make_shared<T>(args...);

只有在以下情况考虑 new:

  1. 需要自定义删除器
  2. 需要调用私有/保护构造函数
  3. 对象很大 + 有长生命周期的 weak_ptr
  4. 需要使用大括号初始化(C++20前的限制)

六、补充:类似的建议

对于unique_ptr,也有类似的建议:

// 推荐autoup=std::make_unique<T>(args...);// 而不是std::unique_ptr<T>up(newT(args...));

原因相同:异常安全 + 代码简洁。


写在最后

看完这篇回答,你可能对 C++ 的智能指针有了更深的理解。但坦白说,玩C++,光了解C++语言特性远远不够的,一定要多做项目

纸上得来终觉浅,绝知此事要躬行。只有在实际项目中遇到过内存泄漏、野指针、性能瓶颈,你才能真正理解为什么要用智能指针,为什么要用 make_shared。

不知道做啥项目的朋友可以看我最近开设的C++项目实战课程:

从7月到现在,我陆续完成了9个C++硬核项目实战课程,已经带领230+同学从零开始实现这些项目。这些同学中有985、211的,也有普通本科的,大家都收获满满。

现有课程列表:

  • 线程池- 理解多线程编程的基础
  • 高性能日志库- 学习异步IO和性能优化
  • 高性能内存池- 深入理解内存管理
  • 多线程下载工具- 综合运用网络编程和并发控制
  • MySQL连接池- 掌握数据库连接管理
  • 内存泄漏检测器- 实战内存管理和调试技术
  • ReactorX项目- 学习高性能网络编程框架
  • 无锁栈+无锁队列(SPSC/MPMC)- 深入无锁编程和并发数据结构
  • 工业级智能指针(shared_ptr)- 从零实现 shared_ptr,彻底理解引用计数和智能指针的内部机制

每个项目都是从0到1手把手带你实现,不只教你怎么用,更教你为什么这么设计,如何优化性能,怎么处理边界情况

对C++项目实战感兴趣的同学可以加我微信详聊:jkfwdkf,备注[项目实战]。

觉得有帮助的话,点个赞和关注再走吧~ 你的支持是我持续输出优质内容的动力!

其他硬核C++项目实战:
从Reactor到网络库:10天打造生产级C++高性能网络库
网上的 shared_ptr 都是玩具?我用半个月造了个工业级的 !
手把手带你实现MPMC无锁队列:6天从Facebook Folly到自研Thunder Queue
C++无锁编程进阶实战:手把手打造极速 SPSC 队列!
C++无锁编程终极实战:手把手带你实现工业级无锁栈!
ReactorX项目火了!腾讯/字节面试官都在问的Reactor模式,终于有人讲透了
被内存泄漏折磨疯了的我,写了个工具,现在同事都来借用…

手撸线程池才是C++程序员的硬实力!7天手把手带你从0到1完整实现
从 0 到 1 实现高性能日志库 MiniSpdlog — 这可能是最适合新手的日志系统实战项目 !
三周肝出4000行代码,我的内存池竟然让malloc"破防"了!性能暴涨7.37倍背后的技术真相
手撸4200行MySQL连接池,8天带你搞定后端核心组件!
终于有人把C++多线程下载工具讲透了!7天手把手带你写出专业级工具

http://www.rkmt.cn/news/127178.html

相关文章:

  • 2025宏观分享:各地经济目标深度拆解与区域分化全景
  • 从Reactor到网络库:10天打造生产级C++高性能网络库
  • AI也会三思而后答?揭秘Self-RAG智能检索术
  • 成都到广州、深圳、东莞、佛山搬家公司专业度排行榜,附搬家费用明细 - 物流人
  • stm32入门篇2 - 实践
  • 时代变迁下的中年职场危机:曾经的红利时代已逝,集体被淘汰的警钟为谁而鸣?
  • 成都到北京、天津、石家庄、唐山搬家公司排行榜,附搬家费用明细 - 物流人
  • 持续集成中的测试策略:构建高效质量保障体系
  • Open-AutoGLM模型获取全攻略(Hugging Face下载避坑指南)
  • 类脑智能技术——数字CMOS型和数模混合CMOS型神经形态芯片以及基于新型器件的混合系统(上)
  • Open-AutoGLM vLLM推理优化实战(99%人不知道的配置技巧)
  • Open-AutoGLM本地部署从入门到精通,企业级安全配置与性能优化全掌握
  • Open-AutoGLM与安卓14兼容性问题终极解决方案:资深工程师亲授调优秘方
  • Open-AutoGLM ModelScope镜像实战(新手必看的8个避坑要点)
  • 为什么你的设备连不上WiFi?,Open-AutoGLM真实案例深度拆解
  • 【Open-AutoGLM高效下载指南】:手把手教你从Hugging Face快速获取模型资源
  • 揭秘Open-AutoGLM离线配置难题:5步实现内网环境全流程闭环部署
  • 【AI本地化新突破】:Open-AutoGLM在千元笔记本上的极限优化实录
  • 【python大数据毕设实战】物联网网络安全威胁数据分析系统、Hadoop、计算机毕业设计、包括数据爬取、数据分析、数据可视化、机器学习、实战教学
  • 【限时揭秘】Open-AutoGLM无线连接核心技术:3分钟实现稳定入网
  • 【Open-AutoGLM优化全攻略】:低配置电脑流畅运行的5大核心技术揭秘
  • 从零到精通:Open-AutoGLM无线调试环境搭建全流程(含安全验证机制解析)
  • 8个AI论文工具,助研究生高效完成毕业论文!
  • 【Open-AutoGLM ModelScope镜像使用全指南】:手把手教你5步部署AI大模型
  • Open-AutoGLM安卓14系统级优化(仅限高端玩家的3个隐藏配置)
  • 2025年自建房建设新风向:引领行业的五大公司推荐,别墅自建房/庭院/院墙/外墙仿石漆/真石漆/自建房建设品牌有哪些 - 品牌推荐师
  • 如何理解多租户架构?
  • Paperzz 降重 / 降 AIGC:从 99.8% 到 14.9%,学术内容的 “合规优化术”
  • 10 个AI论文工具,继续教育学生轻松搞定毕业写作!
  • 2025年AI建筑设计大模机构推荐:聚焦AI设计大模型/AI设计开源模型/AI大模型/BilusAI大模型推荐爆款实力! - 海棠依旧大