C++智能指针构造方式 make_shared和new选择

优先选择make_shared,因其通过单次内存分配提升性能并增强异常安全;当需自定义删除器、管理数组或构造函数非公有时,则必须使用new配合shared_ptr。

c++智能指针构造方式 make_shared和new选择

C++智能指针,特别是

shared_ptr

的构造,在

make_shared

和直接使用

new

表达式之间做选择,这并非一个简单的优劣判断,而更多是基于具体场景的需求和考量。通常,我个人会倾向于

make_shared

,因为它在性能和异常安全方面提供了明显的优势。然而,在某些特定且重要的场景下,我们依然需要依赖

new

来配合

shared_ptr

的构造。

当我们谈论

shared_ptr

的构造时,核心问题其实是:是让

shared_ptr

自己去管理通过

new

分配的内存,还是通过

make_shared

一次性完成对象的构造和控制块的创建?

从我的经验来看,大多数时候,我都会倾向于

make_shared

为什么呢?最直观的感受就是,它写起来更简洁,而且背后隐藏着一个非常重要的优化:单次内存分配。当我们写

new MyObject()

然后用

shared_ptr ptr(new MyObject());

时,实际上发生了两次内存分配:一次是为

MyObject

对象本身,另一次是为

shared_ptr

的控制块(里面包含引用计数、弱引用计数以及类型擦除的删除器等信息)。而

make_shared()

则聪明地将这两者合并成一次分配。这不仅仅是减少了一次系统调用开销,更重要的是,它能更好地利用缓存,减少内存碎片,在高性能场景下,这种差异是能感知到的。

不过,这也不是绝对的。比如,如果我的类构造函数是私有的,或者需要传递一个自定义的deleter(删除器),

make_shared

就显得力不从心了。这时候,我还是得老老实实地用

new

,然后将自定义deleter作为

shared_ptr

构造函数的第二个参数传进去。这就像是,

make_shared

提供了一个非常方便且高效的“套餐”,但如果你需要“定制服务”,就得自己动手了。

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

make_shared

为何能提供性能优势?探究其底层机制

make_shared

的核心优势在于其内存分配策略。当我们使用

shared_ptr p(new T(args));

时,C++运行时需要做两件事:

调用

new T(args)

来在堆上分配

T

类型的内存,并构造对象。

shared_ptr

的构造函数会在堆上再分配一块内存,用于存储控制块(control block)。这个控制块包含了引用计数、弱引用计数以及类型擦除的删除器等信息。这两次独立的内存分配,意味着两次系统调用,以及潜在的内存碎片问题。

make_shared(args)

则不同。它进行的是一次性内存分配。它会计算好

T

对象所需空间和控制块所需空间的总和,然后一次性从堆上申请这么大一块连续的内存。在这块内存中,它会首先构造

T

对象,紧接着就是控制块。这种“合二为一”的做法带来了几个显而易见的优势:

减少内存分配次数: 从两次变为一次,直接降低了系统调用的开销。提高缓存局部性: 对象和其控制块在内存中是相邻的,当访问对象时,控制块的数据很可能也已经被加载到CPU缓存中,从而提高访问效率。减少内存碎片: 连续的内存块管理起来更高效,减少了小块内存反复分配和释放造成的内存碎片化问题。

从我的角度来看,这就像是打包服务。你单独买个商品,再单独买个包装盒,需要两个交易。而

make_shared

就是直接给你一个已经包装好的商品,一次性搞定。对于频繁创建大量

shared_ptr

的场景,这种优化是实打实的。

什么时候应该优先选择

make_shared

?实用场景分析

绝大多数情况下,

make_shared

都应该是你的首选,特别是当你满足以下条件时:

追求性能优化: 如果你的程序对性能敏感,需要频繁创建和销毁

shared_ptr

make_shared

的单次内存分配优势将非常明显。注重异常安全: 考虑这个表达式:

f(shared_ptr(new T()), shared_ptr(new U()));

。如果

new T()

成功,但

new U()

抛出异常,那么

new T()

分配的内存可能永远无法被

shared_ptr

接管并释放,导致内存泄漏。而

make_shared

则不会有这个问题,因为它在一次操作中完成了对象的创建和智能指针的绑定。例如,

f(make_shared(), make_shared());

,即使第二个

make_shared

失败,第一个

make_shared

创建的对象也会被正确管理。这是我个人非常看重的一点,毕竟代码的健壮性比什么都重要。对象构造函数是公开的:

make_shared

需要直接调用对象的构造函数。如果你的构造函数是私有的或保护的(例如,为了强制使用工厂方法),那么

make_shared

就无法直接使用。不需要自定义删除器:

make_shared

不支持直接指定自定义删除器。如果你需要为

shared_ptr

提供一个非默认的删除逻辑(例如,释放一个C风格的资源句柄,或者将对象归还到对象池),那么你必须使用

new

表达式来构造对象,然后将自定义删除器作为

shared_ptr

构造函数的第二个参数。

简而言之,如果你只是想创建一个普通的

shared_ptr

来管理一个堆上的对象,并且没有特殊的删除需求,

make_shared

就是那个“无脑选”的选项。

new

shared_ptr

结合使用的不可替代场景与考量

尽管

make_shared

有很多优点,但

new

表达式配合

shared_ptr

的构造方式,在某些特定场景下依然是不可或缺的。

自定义删除器(Custom Deleter): 这是最常见也是最重要的一个理由。当你的对象需要非标准的释放逻辑时,比如管理文件句柄、数据库连接、或者需要返回到对象池而不是直接

delete

时,

make_shared

就无能为力了。你必须使用

new

来创建对象,然后将自定义删除器作为

shared_ptr

的第二个构造函数参数传入。

#include #include #include  // For FILE*struct FileCloser {    void operator()(FILE* f) const {        if (f) {            fclose(f);            std::cout << "File closed." << std::endl;        }    }};// 使用new和自定义删除器std::shared_ptr file_ptr(fopen("test.txt", "w"), FileCloser{});// make_shared无法直接支持// std::make_shared(fopen("test.txt", "w"), FileCloser{}); // 编译错误

在我看来,这种场景下,

new

是唯一且正确的选择,它提供了

shared_ptr

的灵活性。

weak_ptr

的内存保留考量(特定情况): 这是一个比较微妙的点。当一个

shared_ptr

的所有引用都消失了,但仍有

weak_ptr

指向它时,

make_shared

分配的内存会保留下来,直到最后一个

weak_ptr

也失效。这是因为

make_shared

将对象和控制块放在一起,如果提前释放对象,控制块就无法访问了。这意味着,即使对象已经逻辑上被销毁,其占用的内存可能仍然无法归还给系统,直到所有

weak_ptr

都过期。而使用

new

分配的对象,当

shared_ptr

的引用计数降到零时,对象内存会立即被释放。控制块的内存则会保留到

weak_ptr

计数为零。在某些内存极其敏感,且

weak_ptr

生命周期可能很长的情况下,这种差异可能值得考虑。不过,这通常是过度优化,除非你真的遇到了内存压力问题。

数组的

shared_ptr

管理: C++17之前,

shared_ptr

对数组的支持并不直接。虽然你可以通过

shared_ptr

来管理数组,但

make_shared

没有对应的

make_shared

版本。所以,如果你需要

shared_ptr

来管理一个动态分配的数组,你仍然需要使用

new[]

,并提供一个自定义删除器(或者依赖C++17后的

shared_ptr

自动处理)。

#include #include // C++11/14 管理数组std::shared_ptr arr_ptr_old(new int[10], [](int* p){ delete[] p; });// C++17及以后,可以这样(但make_shared(10) 仍不可用)std::shared_ptr arr_ptr_cxx17(new int[10]);

当然,对于数组,

std::vector

通常是更好的选择,但如果必须用

shared_ptr

管理原生数组,

new

是必经之路。

总的来说,

make_shared

是日常开发中的“默认选项”,它在性能和异常安全方面提供了显著的优势。但当你的需求超出了

make_shared

的范围,比如需要自定义删除行为,或者在极少数情况下需要精细控制内存生命周期时,

new

结合

shared_ptr

的传统方式依然是不可替代的。选择哪种方式,最终还是取决于对项目具体需求的深刻理解。

以上就是C++智能指针构造方式 make_shared和new选择的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1473641.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何为C++配置代码格式化工具Clang-Format并集成到IDE
上一篇 2025年12月18日 20:38:38
C++里氏替换原则 继承体系设计规范
下一篇 2025年12月18日 20:39:04

相关推荐

  • 一加Pro系列微信收款语音怎么开启?快速设置支付播报的方法

    首先检查微信内“收款小账本”开启语音播报功能,其次确保手机系统给予微信通知权限、关闭勿扰模式、媒体音量正常,并在电池设置中避免微信后台被限制,同时更新微信至最新版本;若需个性化,可通过系统通知渠道单独设置收款通知的声音与优先级,但无法更换播报音色;使用时注意公共场合隐私保护,务必核对屏幕金额以防误报…

    2026年9月22日
    100
  • QQ阅读最新版本_QQ阅读官网下载链接

    QQ阅读最新版本官网下载链接是http://book.qq.com/,该平台提供海量正版小说资源,支持多格式电子书阅读,设有免费专区,并具备个性化设置、跨设备同步及互动社区等功能。 QQ阅读最新版本官网下载链接在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来QQ阅读最新版本的相关信息,感兴…

    2026年9月22日
    000
  • PHP匿名函数怎么用_PHP匿名函数使用场景分析

    PHP匿名函数是无名函数,可作为回调或赋值给变量,常用在数组处理、事件回调、逻辑封装等场景,支持use引入外部变量及fn短语法,结合bindTo可访问对象私有成员。 PHP匿名函数,也叫闭包函数(Closure),是一种没有名称的函数,通常作为回调使用或赋值给变量。它在实际开发中非常灵活,尤其适合用…

    2026年9月22日
    100
  • 抖音专营店怎么添加直播号?怎么把新开的抖音号添加到专营店里

    随着抖音平台社交属性不断增强,内容生态日益丰富,越来越多电商从业者开始在该平台上开展业务。其中,抖音专营店作为电商布局的重要一环,也吸引了大量商家入驻。那么,如何将直播号加入抖音专营店中,让直播成为店铺引流和销售的新工具呢?接下来的内容将为您详细介绍。 一、为什么要在抖音专营店中添加直播号 提升店铺…

    2026年9月22日
    000
  • 为什么建议手动定义Java序列化ID

    手动定义serialVersionUID可确保序列化兼容性,避免因类结构变化导致反序列化失败。Java默认生成的ID依赖类名、字段等信息,编译环境或代码微小改动均使其改变,易引发InvalidClassException。显式声明后,可在兼容性变更时主动控制ID更新,保留原ID则允许旧版本读取新对象…

    2026年9月22日
    200
  • VSCode如何通过调试变量监视列表批量追踪数据变化 VSCode变量监视列表批量追踪的新颖技巧​

    VSCode如何通过调试变量监视列表批量追踪数据变化 VSCode变量监视列表批量追踪的新颖技巧​VSCode如何通过调试变量监视列表批量追踪数据变化 VSCode变量监视列表批量追踪的新颖技巧​VSCode如何通过调试变量监视列表批量追踪数据变化 VSCode变量监视列表批量追踪的新颖技巧​VSCode如何通过调试变量监视列表批量追踪数据变化 VSCode变量监视列表批量追踪的新颖技巧​

    vscode中高效批量追踪数据变化的关键是将监视列表用作表达式求值器,而非仅添加单一变量;2. 可在监视列表中添加复杂对象路径(如user.profile.address.city)、计算表达式(如(a + b) * c)、函数调用(如calculatetotal(items))或条件判断(如myv…

    2026年9月22日 用户投稿
    000
  • mysql安装完如何诊断 mysql慢查询分析与优化方法

    要解决 mysql 慢查询问题,首先要开启慢查询日志,其次使用 mysqldumpslow 分析日志,再通过 explain 查看执行计划,最后根据常见优化建议改进 sql 和索引。具体步骤如下:一、修改配置文件或动态开启慢查询日志,并设置阈值和路径;二、使用 mysqldumpslow 工具分析慢…

    2026年9月22日
    100
  • 主板供电相数对CPU超频稳定性的影响:14相 vs. 20相实测

    20相供电主板在超频下表现更稳,实测显示其VRM温度更低、电压波动更小、性能输出更一致,尤其适合极限超频和高负载场景,而14相供电配合优质用料也能满足主流超频需求,普通用户无需盲目追求高相数。 主板供电相数直接影响CPU在高负载和超频状态下的电压稳定性和温度控制。很多人在选择主板时会看到“14相”或…

    2026年9月22日
    200
  • PHP如何实现视频留言评论_PHP实现视频留言评论功能

    答案:通过数据库设计、前端表单、后端处理和评论展示四步实现PHP视频留言功能。1. 创建comments表存储信息;2. 构建表单提交昵称与评论;3. 用add_comment.php接收并存入数据库;4. 在页面读取并安全输出评论,防止XSS。 要实现视频留言评论功能,PHP可以结合前端页面、数据…

    2026年9月22日
    000
  • 抖音小店如何运营?普通人开店选品与推广的实用策略

    抖音小店如何运营?普通人开店选品与推广的实用策略抖音小店如何运营?普通人开店选品与推广的实用策略抖音小店如何运营?普通人开店选品与推广的实用策略抖音小店如何运营?普通人开店选品与推广的实用策略

    新手做抖音小店最现实的问题是没钱投广告和没专业团队,解决方法是抓住选品和推广两个核心环节。一、选品要找市场需求高且利润合理的商品,避开竞争激烈或太冷门的品类,结合多平台数据测试;二、前期重点用“商品卡”推广,通过短视频展示产品使用场景并挂链接引流,成本低且适合测试;三、适当尝试直播积累经验,但不依赖…

    2026年9月22日 用户投稿
    400
  • Spring Boot 应用中的单元测试、Mockito 和集成测试:最佳实践

    第一段引用上面的摘要: 本文旨在帮助初学者理解在 Spring Boot 应用中何时以及如何使用 JUnit、Mockito 和集成测试。我们将探讨这些测试框架在 Controller、Service 和 Repository 层中的应用,并提供示例说明何时使用 Mockito 模拟对象,以及何时使…

    2026年9月22日
    000
  • mysql如何输入变量值 mysql交互式代码输入步骤详解

    mysql如何输入变量值 mysql交互式代码输入步骤详解mysql如何输入变量值 mysql交互式代码输入步骤详解mysql如何输入变量值 mysql交互式代码输入步骤详解mysql如何输入变量值 mysql交互式代码输入步骤详解

    在mysql命令行中交互式输入变量值可通过预处理语句或用户自定义变量实现。1. 使用预处理语句时,先用prepare定义含占位符的sql语句,再通过set设置变量值,最后用execute执行并传参,完成后需deallocate释放资源;2. 使用用户自定义变量时,直接通过set赋值并在sql语句中引…

    2026年9月22日 用户投稿
    100
  • vivoS系列手机微信收款语音播报怎么设置?配置语音的详细方法

    开启微信收款语音播报需在微信“收付款”中启用“收款语音提醒”并授权麦克风权限;2. vivo手机需在设置中开启微信的自启动、后台运行、通知及麦克风权限以确保功能正常;3. 语音播报延迟或无声可能由网络、手机性能、微信版本、系统模式或第三方软件干扰导致;4. 除微信自带功能外,还可选用第三方收款App…

    2026年9月22日
    600
  • 微信注销无法通过验证怎么解决_微信注销验证失败处理方法

    验证失败需先核对实名信息,确保姓名身份证一致,并通过钱包功能检查或更正;随后切换稳定网络并清除微信缓存重试;若仍无法通过,使用原注册设备登录并尝试注销;同时解除所有第三方授权应用;最后联系微信客服提交身份证明材料等待审核处理。 如果您在尝试注销微信账号时遇到验证失败的问题,可能是由于身份信息不匹配、…

    2026年9月22日
    100
  • windows11怎么开启或关闭Hyper-V虚拟机_windows11虚拟化功能设置教程

    windows11怎么开启或关闭Hyper-V虚拟机_windows11虚拟化功能设置教程windows11怎么开启或关闭Hyper-V虚拟机_windows11虚拟化功能设置教程windows11怎么开启或关闭Hyper-V虚拟机_windows11虚拟化功能设置教程windows11怎么开启或关闭Hyper-V虚拟机_windows11虚拟化功能设置教程

    首先确认硬件支持并开启CPU虚拟化,再根据系统版本通过图形界面或命令行启用Hyper-V,操作后重启生效,最后使用Hyper-V管理器验证状态。 如果您在使用Windows 11时需要运行虚拟机或兼容特定模拟器,可能需要开启或关闭Hyper-V功能。该功能依赖于系统版本和硬件支持,操作后需重启生效。…

    2026年9月22日 用户投稿
    100
  • 日本科幻战斗飞行游戏《G-Rebels》实机宣传片

    日本科幻战斗飞行游戏《G-Rebels》实机宣传片日本科幻战斗飞行游戏《G-Rebels》实机宣传片日本科幻战斗飞行游戏《G-Rebels》实机宣传片日本科幻战斗飞行游戏《G-Rebels》实机宣传片

    日本开发商reakktor studios公开了飞行模拟游戏《g-rebels》的实机宣传视频,该作是一款开放世界战斗飞行模拟游戏,玩家可以一览《g-rebels》中活力四射和密集的城市群,从精英家园伊甸园到饱受战争蹂躏和被海啸淹没的城市威尼斯。 《G-Rebels》将登陆PC Steam平台,目前…

    2026年9月22日 用户投稿
    100
  • 抖音小店网页版怎么登录?抖音我的小店在哪里

    随着抖音电商平台的快速发展,越来越多的商家选择入驻该平台。作为商家运营的重要工具之一,抖音小店网页版为店铺管理带来了诸多便利。那么,如何正确登录抖音小店网页版?又该如何找到“我的小店”?下面将为您详细介绍。 一、为什么需要登录抖音小店网页版? 通过抖音小店网页版,商家可以高效地进行商品管理、订单处理…

    2026年9月22日
    000
  • php-gd怎么应用复古滤镜_php-gd图像怀旧色调处理

    使用PHP-GD库实现复古滤镜主要通过色调偏移和色彩调整模拟老照片效果。1. 色调偏黄褐色:先转灰度,再用imagefilter添加棕黄色调;2. 手动像素级调整:逐像素计算灰度并赋予暖色系值,降低饱和度;3. 增强质感:结合对比度降低与轻微模糊提升真实感;4. 示例流程包括加载图像、应用滤镜、输出…

    2026年9月22日
    200
  • Xbox社区总监身穿PlayStation T恤 宣布未来的《光环》的游戏也会上PS

    Xbox正式宣布,继《光环:战役进化》之后,后续推出的《光环》系列新作也将登陆PlayStation平台,这意味着《光环:无限》的正统续作同样会登陆PS主机。 在Halo Studios发布《光环:战役进化》的直播公告中,社区总监Brian Jarrard身穿印有PlayStation标志的T恤登场…

    2026年9月22日
    000
  • 苹果手机为什么无法连接蓝牙耳机

    先检查蓝牙开关、耳机电量及配对模式,确认设备靠近且无干扰,重启设备后删除旧连接记录并重连,更新系统或重置网络设置,若均无效则可能存在硬件故障需联系官方支持。 苹果手机连不上蓝牙耳机,通常不是大问题,多是设置或环境的小疏忽。先别着急,按下面几步检查,基本能解决大部分情况。 基础状态确认 连接失败往往出…

    2026年9月22日
    000

发表回复

登录后才能评论
关注微信