C++如何使用make_unique创建unique_ptr对象

std::make_unique 是 C++14 引入的辅助函数,用于安全、简洁地创建 std::unique_ptr 对象,避免手动使用 new 导致的内存泄漏和异常不安全问题。它通过单一表达式完成对象构造和智能指针初始化,确保异常安全,同时提升代码可读性。对于单个对象和数组类型均支持,但数组仅能接受大小参数并进行默认初始化;不支持自定义删除器,需直接使用 unique_ptr 构造函数处理特殊资源释放。在现代 C++ 中,std::make_unique 与 std::unique_ptr 共同实现 RAII 原则,确保资源的自动管理和程序的健壮性,是动态内存管理的推荐方式。

c++如何使用make_unique创建unique_ptr对象

std::make_unique

是 C++14 引入的一个非常实用的辅助函数,它提供了一种更安全、更简洁的方式来创建

std::unique_ptr

对象,有效地避免了直接使用

new

关键字可能带来的内存泄漏风险和重复代码,并且在异常安全方面表现更优。

解决方案

要使用

std::make_unique

创建

unique_ptr

对象,你只需要将你想要创建的类型作为模板参数,并将该类型的构造函数所需的所有参数直接传递给

make_unique

函数即可。

基本用法示例:

#include #include #include class MyObject {public:    MyObject() {        std::cout << "MyObject default constructor called." << std::endl;    }    MyObject(int id, const std::string& name) : id_(id), name_(name) {        std::cout << "MyObject constructor with args called: ID=" << id_ << ", Name=" << name_ << std::endl;    }    ~MyObject() {        std::cout << "MyObject destructor called. ID=" << id_ << std::endl;    }    void greet() const {        std::cout << "Hello from MyObject! ID=" << id_ << ", Name=" << name_ << std::endl;    }private:    int id_ = 0;    std::string name_ = "Default";};int main() {    // 1. 创建一个不带参数的MyObject    // 这等价于 std::unique_ptr(new MyObject());    std::unique_ptr obj1 = std::make_unique();    obj1->greet();    std::cout << "--------------------" << std::endl;    // 2. 创建一个带参数的MyObject    // 这等价于 std::unique_ptr(new MyObject(101, "Alice"));    std::unique_ptr obj2 = std::make_unique(101, "Alice");    obj2->greet();    std::cout << "--------------------" << std::endl;    // 3. 创建一个MyObject数组    // 这等价于 std::unique_ptr(new MyObject[3]);    // 数组元素会被默认初始化或值初始化    std::unique_ptr objArray = std::make_unique(3);    // 访问数组元素,例如:objArray[0].greet();    // 注意:对于数组,make_unique只接受大小参数,不接受单个元素的构造函数参数。    // 如果需要自定义数组元素的构造,可能需要手动循环或考虑std::vector。    std::cout << "Exiting main scope." << std::endl;    // 当unique_ptr超出作用域时,它所管理的内存会被自动释放。    return 0;}

这段代码清晰地展示了

make_unique

如何简化了

unique_ptr

的创建过程。你不需要手动写

new

,也不需要关心

delete

,所有都由

unique_ptr

及其辅助函数搞定。

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

为什么推荐使用

std::make_unique

而不是直接

new

来创建

unique_ptr

这其实是一个非常关键的问题,不仅仅是代码看起来更简洁那么简单,背后还有深层次的设计考量和实际益处。从我个人的经验来看,坚持使用

make_unique

几乎能避免掉一类常见的、难以发现的资源泄漏问题。

首先,最核心的原因是异常安全性。考虑这样一个场景:

void process(std::unique_ptr res, AnotherObject obj); // 假设AnotherObject构造函数可能抛异常// 潜在问题代码:process(std::unique_ptr(new Resource()), AnotherObject());

process

函数调用中,参数的评估顺序是不确定的。编译器可能会先执行

new Resource()

,然后执行

AnotherObject()

,最后才构造

std::unique_ptr

。如果

new Resource()

成功分配了内存,但紧接着

AnotherObject()

的构造函数抛出了异常,那么

std::unique_ptr

就永远没有机会被构造出来,自然也就无法接管并释放

new Resource()

分配的内存。结果就是,

Resource

对象发生了内存泄漏。

而使用

std::make_unique

,情况就完全不同了:

process(std::make_unique(), AnotherObject());
std::make_unique()

是一个单一的表达式,它要么完全成功(分配内存并构造

unique_ptr

),要么完全失败(如果

Resource

构造函数抛异常)。它确保了在

unique_ptr

构造完成之前,所有相关的内存分配和对象构造都已妥善处理。这样,即使

AnotherObject()

抛出异常,

unique_ptr

也已经成功接管了

Resource

的所有权,并在

process

函数调用失败时,通过栈展开机制,

unique_ptr

的析构函数会被调用,从而安全地释放内存。这大大增强了代码的健壮性。

其次,是代码的简洁性和可读性。对比

std::unique_ptr(new MyObject(arg1, arg2))

std::make_unique(arg1, arg2)

,后者显然更短,重复的类型名也少了一次。虽然这看起来是小事,但在大型项目中,这种累积的简洁性能够显著提升代码的可读性和维护效率。它减少了“仪式性”代码,让开发者能更专注于业务逻辑本身。

最后,虽然对

unique_ptr

来说影响不如

shared_ptr

那么大,但

make_unique

也避免了两次内存分配的可能性。

unique_ptr

本身通常不需要额外的控制块,但

make_unique

仍然将对象本身的内存分配和

unique_ptr

的构造过程封装在一个调用中,避免了手动

new

带来的潜在分离风险。

std::make_unique

在处理数组和自定义删除器时有什么需要注意的吗?

是的,

std::make_unique

在处理数组和自定义删除器时确实有一些特定的规则和限制,理解这些对于正确使用它至关重要。

处理数组:

std::make_unique

可以用来创建

unique_ptr

管理的动态数组。但它的语法和行为与单个对象的创建略有不同。

当你需要创建数组时,类型参数应该是一个“数组类型”,并且需要提供数组的大小:

std::unique_ptr intArray = std::make_unique(10); // 创建一个包含10个int的数组for (int i = 0; i < 10; ++i) {    intArray[i] = i * 2;}// 当 intArray 超出作用域时,会自动调用 delete[] 释放内存

这里需要注意的关键点是:

类型声明:必须使用

T[]

而不是

T

构造函数参数

make_unique

只接受一个参数,即数组的大小。它会使用默认构造函数(或值初始化)来初始化数组中的所有元素。这意味着你不能直接通过

make_unique

为数组中的每个元素传递不同的构造函数参数。如果你需要对数组的每个元素进行复杂的初始化,通常的做法是先创建一个

std::vector

,或者手动循环初始化

unique_ptr

管理的内存。自动

delete[]

std::unique_ptr

会自动使用

delete[]

来释放内存,这正是我们期望的数组内存释放方式,避免了常见的

delete

vs

delete[]

错误。

处理自定义删除器:

这是

std::make_unique

的一个重要限制:它不支持自定义删除器。

std::make_unique

的设计目标是简化最常见的

unique_ptr

使用场景,即管理通过

new

分配的、需要通过

delete

delete[]

释放的内存。自定义删除器允许你指定一个非标准的资源释放机制(例如关闭文件句柄、释放操作系统资源等)。

如果你需要使用自定义删除器,你必须直接调用

std::unique_ptr

的构造函数,将自定义删除器作为模板参数和构造函数参数传递进去。

例如,管理一个

FILE*

资源:

#include  // For FILE, fopen, fclose// 自定义删除器,用于关闭文件struct FileCloser {    void operator()(FILE* filePtr) const {        if (filePtr) {            std::cout << "Closing file..." << std::endl;            fclose(filePtr);        }    }};int main() {    // 错误:make_unique 不支持自定义删除器    // std::unique_ptr pFile = std::make_unique(fopen("test.txt", "w"));    // 正确:直接使用 unique_ptr 构造函数    // 模板参数是     // 构造函数参数是 (原始指针, 删除器对象)    std::unique_ptr pFile(fopen("test.txt", "w"), FileCloser());    if (pFile) {        fprintf(pFile.get(), "Hello from unique_ptr!\n");        std::cout << "File opened and written to." << std::endl;    } else {        std::cerr << "Failed to open file." << std::endl;    }    // 当 pFile 超出作用域时,FileCloser::operator() 会被调用    std::cout << "Exiting main scope for custom deleter example." << std::endl;    return 0;}

在这个例子中,

FileCloser

是一个函数对象(或lambda表达式也可以),它定义了如何关闭

FILE*

。我们通过

std::unique_ptr(...)

明确地告诉

unique_ptr

要使用

FileCloser

来释放资源。所以,当遇到需要非标准释放逻辑的资源时,请记住

make_unique

并非万能,你需要回到

unique_ptr

的直接构造函数。

unique_ptr

make_unique

在现代 C++ 开发中扮演着怎样的角色?

在现代 C++ 开发中,

unique_ptr

make_unique

组合起来,几乎是动态内存管理的首选方案,它们在编写安全、高效且易于维护的代码方面扮演着不可或缺的角色。我甚至可以说,如果你还在频繁地直接使用

new

delete

来管理独占资源,那么你的 C++ 代码风格可能需要更新了。

它们的核心作用在于实现 RAII (Resource Acquisition Is Initialization) 原则。

unique_ptr

的设计哲学就是将资源的生命周期与对象的生命周期绑定。当

unique_ptr

对象被创建时,它获取资源(通过

new

或其他方式),当

unique_ptr

对象被销毁时(无论是正常退出作用域、抛出异常还是容器销毁),它都会自动释放所持有的资源。这种机制彻底解决了传统 C++ 中手动

new

/

delete

带来的两大痛点:内存泄漏和重复释放。

unique_ptr

的主要角色:

独占所有权管理

unique_ptr

明确表示它对所指向的资源拥有独占所有权。这意味着同一时间只有一个

unique_ptr

可以指向某个特定资源。这种清晰的所有权语义极大地简化了代码的推理,避免了多个指针同时管理同一块内存可能导致的混乱和错误。移动语义

unique_ptr

是一个“只可移动,不可复制”的类型。这意味着你可以通过

std::move

将所有权从一个

unique_ptr

转移到另一个,但不能复制它。这种设计强制了所有权的单一性,使得资源转移变得安全且高效,特别是在函数返回动态分配对象时。替代裸指针:在现代 C++ 中,裸指针(raw pointer)通常只用于非拥有性引用(non-owning reference)或者指向数组的迭代器,而不再是管理动态内存的主要工具

unique_ptr

几乎完全取代了需要

delete

的裸指针。异常安全:如前所述,

unique_ptr

配合

make_unique

提供了强大的异常安全性。无论程序执行路径如何,资源总能被正确释放。Pimpl (Pointer to Implementation) 模式

unique_ptr

是实现 Pimpl 模式的理想选择。通过

unique_ptr

作为类成员,可以隐藏类的实现细节,减少编译依赖,从而加快编译速度,并提供更好的二进制兼容性。

make_unique

的主要角色:

简化

unique_ptr

创建:它提供了一个简洁、一致的接口来创建

unique_ptr

提升异常安全:这是它最关键的贡献,通过单次操作保证了内存分配和

unique_ptr

构造的原子性,避免了资源泄漏。鼓励最佳实践

make_unique

的存在本身就在鼓励开发者采用更安全的

unique_ptr

创建方式,而不是回退到

new

总而言之,

unique_ptr

make_unique

是现代 C++ 中构建健壮、安全和高效应用程序的基石。它们将资源管理从手动、易错的任务提升为编译器和库自动处理的机制,让开发者能够将更多精力放在解决实际业务问题上,而不是与内存泄漏和悬空指针搏斗。它们不仅仅是语法糖,更是 C++ 资源管理哲学的一次深刻实践。

以上就是C++如何使用make_unique创建unique_ptr对象的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
C++如何在文件操作中使用fstream管理读写
上一篇 2025年12月18日 22:09:37
C++volatile关键字在内存访问中的作用
下一篇 2025年12月18日 22:09:57

相关推荐

  • MySQLCPU资源消耗优化_MySQL减少查询CPU使用技巧

    MySQLCPU资源消耗优化_MySQL减少查询CPU使用技巧MySQLCPU资源消耗优化_MySQL减少查询CPU使用技巧MySQLCPU资源消耗优化_MySQL减少查询CPU使用技巧MySQLCPU资源消耗优化_MySQL减少查询CPU使用技巧

    mysql cpu占用率高的核心原因是低效sql查询、缺失索引、高并发连接和不合理配置。1. 优化sql语句,避免全表扫描、复杂join和函数操作,使用explain分析执行计划;2. 合理创建索引,提升查询效率;3. 控制并发连接,减少线程切换开销;4. 调整innodb_buffer_pool_…

    2026年9月3日 用户投稿
    000
  • win11无法安全删除USB大容量存储设备怎么办_U盘无法弹出问题解决方法

    当U盘提示“设备正在使用中”时,可通过关闭占用程序、重启资源管理器、使用diskpart命令脱机、更改删除策略、事件查看器排查或设备管理器卸载设备来解决。 如果您尝试通过系统托盘图标或“此电脑”窗口安全弹出U盘时,系统提示“设备正在使用中”,则可能是由于后台程序、服务或系统进程占用了该设备。以下是解…

    2026年9月3日
    300
  • B端设备租赁:企业资产管理App应用开发

    在工程、医疗、it服务等设备密集型行业中,b端设备租赁正逐渐成为企业优化资金流动、灵活应对项目需求的重要方式。然而,面对数量庞大、状态多变、分布广泛的租赁设备资产,如何有效管理、避免闲置浪费与调度混乱、减少维护滞后等问题,成为众多企业亟需解决的难题。此时,开发专业的企业资产管理app应用便成为突破困…

    2026年9月3日
    300
  • 点淘直播间怎么进入_点淘直播间如何进入观看互动购物教程

    1、打开点淘APP点击首页,滑动查找直播卡片进入;2、通过顶部搜索框输入主播或商品名,切换至直播页进入;3、在“我的”关注列表中查看正在直播的主播;4、从商品详情页的直播提示按钮跳转进入直播间。 如果您想通过点淘APP观看直播并参与互动购物,但不清楚如何进入直播间,可能是由于入口位置不熟悉或操作流程…

    2026年9月3日
    000
  • 我的世界不可获得物品有哪些 不可获得物品种类介绍

    我的世界不可获得物品有哪些 不可获得物品种类介绍我的世界不可获得物品有哪些 不可获得物品种类介绍我的世界不可获得物品有哪些 不可获得物品种类介绍我的世界不可获得物品有哪些 不可获得物品种类介绍

    我的世界中存在一些在生存模式下无法正常获取的特殊物品,主要包括屏障方块、结构方块和命令方块,以下是对这些物品及其特点的具体介绍。 我的世界不可获得物品具体如下: 一、屏障方块属于生存模式中无法正常获取的物品,除非使用作弊手段。该方块在创造模式中可以找到,放置后呈隐形状态,常用于地图设计中控制玩家活动…

    2026年9月3日 用户投稿
    100
  • 高效解决 Laravel Eloquent 关联查询中的大小写问题

    最近在开发一个 laravel 项目时,遇到了一个令人头疼的 bug。我的数据库使用了区分大小写的字符集,而 item_tag 表中的 item_uuid 字段存储的是字符串类型的 uuid。 item 模型和 tag 模型之间存在多对多关系。当我使用 item::with(‘tags’)->…

    用户投稿 2026年9月3日
    000
  • 电脑重新做系统后原来的软件怎么恢复

    一、准备工作 在开始恢复原有软件之前,需要提前准备好一些必要的工具。首先,务必备份好个人的重要文件和资料,防止数据丢失。其次,准备一个可用的系统安装盘或U盘,以便进行系统重装。此外,保持网络连接稳定也很重要,这样可以方便后续下载和安装软件。 二、具体操作 1. 重新下载并安装 大多数软件都可以通过官…

    2026年9月3日
    300
  • 今日头条直播间怎么送礼物_今日头条直播互动礼物赠送教程

    1、在今日头条直播间送礼物可直接表达对主播的支持。2、通过手机APP进入直播间,点击“礼物”按钮选择并送出礼物,余额不足时可充值。3、使用PC端直播伴侣时,观众仍需用手机完成送礼操作,礼物信息会同步至电脑端。4、参与福袋活动虽不能直接送礼,但中奖后可获得平台奖励,增强互动体验。 如果您想在今日头条直…

    2026年9月3日
    200
  • 告别支付难题:使用Softon/Indipay 简化印度支付网关集成

    在为印度市场开发一个电商应用时,我面临着一个巨大的挑战:如何高效地集成多个印度支付网关,例如ccavenue、payumoney、paytm等等。每个网关都有其独特的api和参数要求,单独集成每个网关不仅费时费力,而且容易出错。维护和更新这些集成也变得异常困难。 我最初尝试分别集成每个网关,但很快发…

    用户投稿 2026年9月3日
    200
  • 三家基础电信企业全面接入 DeepSeek开源大模型

    ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 工业和信息化部消息,2025年春节期间,我国通信网络运行稳定,通信服务持续优化,人工智能赋能成为服务创新的重要驱动力。 三大基础电信运营商已全面接入DeepSeek开源大模型,并在多个场景和产品…

    2026年9月3日
    100
  • Dubbo接口参数需实现Serializable,而Spring MVC无需:为什么?

    Dubbo与Spring MVC参数序列化差异 Dubbo和Spring MVC在处理接口请求参数序列化方面存在显著区别。这种差异的核心在于它们各自的架构和数据传输方式。 Dubbo参数序列化:必须实现Serializable接口 Dubbo框架采用远程过程调用(RPC)机制,在不同的JVM之间进行…

    2026年9月3日
    100
  • win10文件权限怎么修改_win10文件夹所有权与权限修改教程

    1、通过属性-安全-高级更改文件夹所有者为当前用户,并替换子容器所有权;2、编辑安全权限,为当前用户添加完全控制允许权限;3、使用管理员命令提示符执行takeown和icacls命令获取所有权并赋予权限;4、在高级安全设置中禁用继承,转换并自定义显式权限。 如果您尝试访问或修改某个文件或文件夹,但系…

    2026年9月3日
    100
  • Podman 已成 Linux 官方标配!Docker 没戏了?

    Podman 已成 Linux 官方标配!Docker 没戏了?Podman 已成 Linux 官方标配!Docker 没戏了?Podman 已成 Linux 官方标配!Docker 没戏了?Podman 已成 Linux 官方标配!Docker 没戏了?

    podman 简介 Podman 是一个开源项目,在 GitHub 上已获得超过 12,000 个星标,可以在大多数 Linux 平台上运行。Podman 是一个无守护进程的容器引擎,用于在 Linux 系统上开发、管理和运行 OCI(开放容器倡议)容器和容器镜像。Podman 提供了与 Docke…

    2026年9月3日 用户投稿
    300
  • win10任务栏卡死点不动怎么办_win10任务栏无响应卡死解决方法

    任务栏无法点击可尝试重启Windows资源管理器,禁用资讯和兴趣功能,使用PowerShell重新部署应用包,关闭多显示器任务栏同步,或运行SFC扫描修复系统文件。 如果您在使用Windows 10时发现任务栏无法点击或响应迟缓,这可能是由于系统资源管理器异常、功能冲突或后台进程占用过高导致的。以下…

    2026年9月3日
    200
  • 慧通差旅联合IDC行业首发《央国企数智化差旅管理白皮书》,解码央国企转型新动能

    6月21日,华为开发者大会2025期间,慧通差旅联合idc及百余位央国企客户、开发者、生态伙伴共聚东莞松山湖,举办“数智化转型差旅同行”专题论坛,重磅发布行业首份《央国企数智化差旅管理白皮书》(以下简称白皮书),深度解码从顶层规划到落地实施的央国企数智差旅建设之道。同步展示全新升级的差旅agent等…

    2026年9月3日
    200
  • 余承东称华为智驾在行业内断代领先 本月揭晓尊界黑科技

    华为余承东近日预告,华为鸿蒙智行将在智能驾驶领域带来革命性突破,并称其与“凑合能用”的水平有着本质区别,如同5g网络与普通网络的差异。 本月,华为将公布尊界系列的核心技术,展现智慧出行的未来图景。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜…

    2026年9月3日
    100
  • VSCode怎么写CSS文件_VSCode编写和预览CSS样式表详细教程

    VSCode怎么写CSS文件_VSCode编写和预览CSS样式表详细教程VSCode怎么写CSS文件_VSCode编写和预览CSS样式表详细教程VSCode怎么写CSS文件_VSCode编写和预览CSS样式表详细教程VSCode怎么写CSS文件_VSCode编写和预览CSS样式表详细教程

    首先新建.css文件并编写样式,通过链接HTML或使用Live Server插件实时预览;推荐使用CSS Peek、IntelliSense、Prettier等插件提升效率;调试可借助浏览器开发者工具,大型项目建议采用Sass、BEM命名和模块化管理。 VSCode编写CSS文件,简单来说,就是新建…

    2026年9月3日 用户投稿
    300
  • 探探修改定位怎么实现_探探虚拟定位异地交友教程

    可通过虚拟定位实现探探异地交友。①使用“Fake GPS Location”等App修改iPhone定位;②用雷电模拟器在电脑运行探探并篡改GPS;③技术用户可重签名IPA注入定位模块,持久化伪装位置。 如果您希望在探探上实现异地交友,但受限于当前地理位置无法匹配到目标城市用户,则可以通过修改定位来…

    2026年9月3日
    200
  • Java数组中如何判断是否存在指定元素?

    Java数组元素存在性检测 在Java开发中,经常需要判断数组中是否存在特定元素。 对于对象数组,可以使用Arrays.asList()方法结合contains()方法便捷地实现;但对于基本类型数组,则需要采用迭代的方式进行查找。 基本类型数组的判断 对于boolean、int、double等基本类…

    2026年9月3日
    200
  • HDC2025鲸鸿动能品牌增长沙龙:拿回营销主动权,驱动确定性增长

    HDC2025鲸鸿动能品牌增长沙龙:拿回营销主动权,驱动确定性增长HDC2025鲸鸿动能品牌增长沙龙:拿回营销主动权,驱动确定性增长HDC2025鲸鸿动能品牌增长沙龙:拿回营销主动权,驱动确定性增长HDC2025鲸鸿动能品牌增长沙龙:拿回营销主动权,驱动确定性增长

    6月19日,hdc2025分论坛期间,鲸鸿动能举办了品牌增长主题沙龙。来自多个行业的资深人士,包括知名品牌代表、创意先锋及技术企业负责人齐聚一堂,围绕“确定性增长”这一核心议题,展开深入交流,共同探索如何重掌营销主动权。 在当前多变的市场环境中,“营销失权”成为众多品牌面临的难题。企业常常陷入被动应…

    2026年9月3日 用户投稿
    100

发表回复

登录后才能评论
关注微信