C++ std::make_unique详解-安全创建unique_ptr的官方方法

C++ std::make_unique详解-安全创建unique_ptr的官方方法

C++ std::make_unique详解-安全创建unique_ptr的官方方法

一、C++ std::make_unique详解

1、什么是 std::make_unique

std::make_unique 是 C++14 标准库中引入的一个模板函数,用于创建并返回一个指向动态分配对象的 std::unique_ptr 智能指针。它的主要目的是提供一种更安全、更简洁的方式来创建和管理独占所有权的动态对象。

2、为什么需要 std::make_unique? (与直接 new 对比)

std::make_unique 出现之前,创建 std::unique_ptr 通常是这样做的:

std::unique_ptr<MyClass>ptr(newMyClass(arg1, arg2));

这种方式存在一些潜在问题:

  • 异常安全风险: 如果在 new 操作和 unique_ptr 构造之间发生了异常(例如在计算构造函数参数时),那么 new 分配的内存可能无法被 unique_ptr 接管,从而发生内存泄漏。
  • 代码冗余: 需要重复书写类型 MyClass
  • 风格一致性: C++11 提供了 std::make_shared 来创建 std::shared_ptr,缺少 std::make_unique 显得不完整。

std::make_unique 解决了这些问题:

auto ptr = std::make_unique<MyClass>(arg1, arg2);
  • 异常安全:std::make_unique 在内部一次性完成内存分配和对象构造。如果构造函数参数的计算过程中抛出异常,或者构造函数本身抛出异常,因为此时还没有返回 unique_ptr,所以不会有内存泄漏(分配的内存会被自动释放)。
  • 简洁性: 使用 auto 关键字,避免了类型重复书写,代码更简洁。
  • 一致性:std::make_shared 的使用方式保持一致。
  • 效率: 编译器有机会进行优化。

3、基本语法和用法

std::make_unique 有两种主要形式:

    • 数组版本返回的是 std::unique_ptr<T[]>,这与指向单个对象的 std::unique_ptr<T> 是不同的特化。
    • 数组元素会被值初始化(基本类型初始化为 0,类类型调用默认构造函数)。无法在创建数组时指定每个元素的构造参数。
    • 访问数组元素使用 operator[]arr[i] = 42;

创建动态数组:

template<typenameT> std::unique_ptr<T[]> std::make_unique(std::size_t size);

用法:

// 创建一个包含 10 个默认构造的 int 的动态数组auto arr = std::make_unique<int[]>(10);// 创建一个包含 5 个默认构造的 Widget 的动态数组auto widgetArr = std::make_unique<Widget[]>(5);

注意:

创建单个对象:

template<typenameT,typename... Args> std::unique_ptr<T> std::make_unique(Args&&... args);

用法:

// 创建默认构造的 Widgetauto widget1 = std::make_unique<Widget>();// 创建带参数的 Widgetauto widget2 = std::make_unique<Widget>(100,"Hello");// 创建派生类对象 (多态)auto derived = std::make_unique<Derived>(some_arg); std::unique_ptr<Base> basePtr = std::move(derived);// 所有权转移

4、关键特性

  • 所有权独占:std::make_unique 创建的 std::unique_ptr 拥有对所创建对象的独占所有权。这意味着同一时刻只有一个 unique_ptr 可以指向该对象。所有权可以通过 std::move 转移。
  • 自动内存管理:unique_ptr 被销毁(例如离开作用域)时,它所管理的对象会被自动删除(调用其析构函数并释放内存)。
  • 不支持自定义删除器:std::make_unique不支持指定自定义删除器。如果需要自定义删除器,必须直接使用 std::unique_ptr 的构造函数,例如 std::unique_ptr<T, D> ptr(new T, custom_deleter);
  • 不能用于 std::shared_ptrstd::make_unique 只创建 std::unique_ptr。要创建 std::shared_ptr,应使用 std::make_shared

5、总结与建议

  • 优先使用 std::make_unique 在 C++14 及以后的代码中,优先使用 std::make_unique 来创建 std::unique_ptr。它提供了更好的异常安全性和更简洁的代码。
  • 理解所有权: 清楚 unique_ptr 的独占所有权语义,并通过 std::move 来转移所有权。
  • 数组特化: 需要创建动态数组时,使用 std::make_unique<T[]>(size) 形式。
  • 自定义删除器的限制: 如果需要自定义删除器,则不能使用 std::make_unique

二、示例代码

1、示例代码1

#include<iostream>#include<memory>#include<string>classMyClass{public:MyClass(int id, std::string name):id_(id),name_(std::move(name)){ std::cout <<"MyClass constructed: "<< id_ <<", "<< name_ <<"\n";}~MyClass(){ std::cout <<"MyClass destroyed: "<< id_ <<", "<< name_ <<"\n";}voidprint()const{ std::cout <<"ID: "<< id_ <<", Name: "<< name_ <<"\n";}private:int id_; std::string name_;};intmain(){// 创建单个对象auto obj = std::make_unique<MyClass>(1,"Alice"); obj->print();// 创建动态数组 (5 个 int)auto numbers = std::make_unique<int[]>(5);for(int i =0; i <5;++i){ numbers[i]= i * i; std::cout << numbers[i]<<" ";} std::cout <<"\n";// 所有权转移auto newOwner = std::move(obj);if(!obj){ std::cout <<"obj is now nullptr\n";} newOwner->print();// 离开作用域时,newOwner 和 numbers 管理的对象会被自动销毁return0;}

输出:

MyClass constructed: 1, Alice ID: 1, Name: Alice 0 1 4 9 16 obj is now nullptr ID: 1, Name: Alice MyClass destroyed: 1, Alice 
在这里插入图片描述

2、示例代码2

#include<memory>// 包含 std::make_unique 和 std::unique_ptr#include<iostream>// 定义一个简单的自定义类classPoint{public:Point(int x,int y):x_(x),y_(y){}voidprint()const{ std::cout <<"Point("<< x_ <<", "<< y_ <<")\n";}private:int x_;int y_;};intmain(){// 示例 1: 创建一个 std::unique_ptr 指向 int 类型auto num_ptr = std::make_unique<int>(42);// 使用 std::make_unique 分配内存 std::cout <<"整数值: "<<*num_ptr << std::endl;// 解引用指针输出值// 示例 2: 创建一个 std::unique_ptr 指向自定义类 Pointauto point_ptr = std::make_unique<Point>(3,4);// 传递构造函数参数 point_ptr->print();// 调用成员函数return0;}

输出:

整数值:42Point(3,4)
在这里插入图片描述


在这里插入图片描述

Read more

【源力觉醒 创作者计划】文心开源大模型ERNIE-4.5私有化部署保姆级教程与多功能界面窗口部署

【源力觉醒 创作者计划】文心开源大模型ERNIE-4.5私有化部署保姆级教程与多功能界面窗口部署

* 按照我这个路线来部署,网速快五分钟就能零基础跑通模型 一起来轻松玩转文心大模型吧👉一文心大模型免费下载地址: https://ai.gitcode.com/theme/1939325484087291906 计算机配置 组件配置GPUNVIDIA A8000 SXM4 80GB × 1CPU15 核处理器内存249GB 内存硬盘系统盘 100GB + 数据盘 50GB * 部署使用的电脑都是只有系统的云电脑,部署过程中的性能差异,评估它们的运行效率和资源消耗,从而为不同需求的开发者和研究者提供参考依据。 * 文心模型汇总 环境配置与部署 1. 更换镜像源(使用阿里云镜像源): sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i 's|http://archive.ubuntu.com/

By Ne0inhk
解决Markdown笔记图片失效问题:Gitee+PicGo图床搭建全攻略

解决Markdown笔记图片失效问题:Gitee+PicGo图床搭建全攻略

引言:为什么要解决搭建图床? 你是否遇到过这样的场景: * 用 Obsidian 写了半年的知识库,换电脑时发现 所有图片都变成 “破碎图标”; * 把 Markdown 笔记分享给同事,对方打开后 图片全是本地路径,根本看不到内容; * 尝试用云盘链接替代,却因为 “防盗链” 或 “链接过期”,图片还是无法正常显示…… 本地 Markdown 笔记的 “图片依赖本地路径”,是困扰无数创作者的痛点。而解决这个问题的核心,就是搭建一个 “图床” —— 把图片托管到云端,让链接永远有效。 本文将带你用 “Gitee(国内免费仓库)+ PicGo(自动上传工具)+ Node.js(运行环境)” 搭建图床,不仅解决 “图片失效”,还能实现: * ✔️ 国内访问快:Gitee 服务器在国内,无需科学上网,图片秒加载; * ✔️ 完全免费:Gitee

By Ne0inhk
深度盘点:GitHub 上十大必装 Claude Skill,让你的 AI 助手效率提升 4 倍

深度盘点:GitHub 上十大必装 Claude Skill,让你的 AI 助手效率提升 4 倍

深度盘点:GitHub 上十大必装 Claude Skill,让你的 AI 助手效率提升 4 倍 Claude Code 已经很强大,但如果搭配这些精心设计的 Skills,它将变身超级生产力工具。本文为你深度解析 GitHub 上最受欢迎的 10 大 Claude Skills,帮助你找到最适合的配置方案。 引言:为什么 Claude Skills 如此重要? 在 2025-2026 年,Claude Code 生态经历了爆发式增长。Skills 系统的出现,让 Claude 从一个"对话助手"升级为"专业工具"。通过安装不同的 Skills,你可以:

By Ne0inhk
Git 到底是干啥的?零基础小白听完都懂了并且轻松驾驭它

Git 到底是干啥的?零基础小白听完都懂了并且轻松驾驭它

git,通俗的来说就是一种用来多人文件版本合作的工具,但是对一些非程序员的项目小白或者没有程序基础的但是想要入行做程序员的人来说,完完全全理解起来稍微有点困难。这篇文章不像很多文章一样是枯涩的码字教学。现在,我们就用最通俗易懂的方式,让你从零基础理解他,并且使用他。这种教学方法不是把你当白痴的教学方法,反而是让你快速入门深刻理解它,并记住它的教学方法。因为可能说得比较详细,篇幅较长,还得请你耐心的把他看完。 一、git的作用 1、git的版本控制 文件永远不会只有一个版本,这句话我们似乎用亲身经历证明过。你是否有过以下经历👇 📘论文会有“终稿v1、终稿v2、终稿最终版”、 ✍设计稿会有“改版A、改版B、改版C”、 🧺甚至自己写的文章也会来回改十几遍。 🥚更不用说单独只通过一个本地夹操刀一个大型项目了 突然有一天你觉得你的论文、设计稿、文章、项目某一个节点开始脱离了原本的方向或者发生了一些错误,但是你已经对其进行多处修改了,单独再修改不仅费事废经历,还容易发生遗漏。 你或许信誓旦旦的告诉我,你可以这样做。。。👇 论文_最终v1.docx 论文_

By Ne0inhk