跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

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

C++14 标准中的 std::make_unique 函数,用于安全创建 std::unique_ptr 智能指针。通过与直接 new 操作对比,阐述了其在异常安全性、代码简洁性和风格一致性方面的优势。文章涵盖了基本语法、数组版本用法、关键特性如独占所有权和自动内存管理,并提供了完整的示例代码展示单个对象及动态数组的创建与管理过程。建议在新代码中优先使用此方法,同时注意其不支持自定义删除器的限制。

ServerBase发布于 2026/3/25更新于 2026/9/562 浏览
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(new MyClass(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<typename T> 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<typename T, typename... Args> std::unique_ptr<T> std::make_unique(Args&&... args);

用法:

// 创建默认构造的 Widget
auto widget1 = std::make_unique<Widget>();
// 创建带参数的 Widget
auto 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_ptr: std::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>

class MyClass {
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";
    }
    void print() const {
        std::cout << "ID: " << id_ << ", Name: " << name_ << "\n";
    }
private:
    int id_;
    std::string name_;
};

int main() {
    // 创建单个对象
    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 管理的对象会被自动销毁
    return 0;
}

输出:

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>

// 定义一个简单的自定义类
class Point {
public:
    Point(int x, int y) : x_(x), y_(y) {}
    void print() const {
        std::cout << "Point(" << x_ << ", " << y_ << ")\n";
    }
private:
    int x_;
    int y_;
};

int main() {
    // 示例 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 指向自定义类 Point
    auto point_ptr = std::make_unique<Point>(3, 4);
    // 传递构造函数参数
    point_ptr->print();

    return 0;
}

输出:

整数值:42
Point(3, 4)

目录

  1. 一、C++ std::make_unique 详解
  2. 1、什么是 std::make_unique?
  3. 2、为什么需要 std::make_unique? (与直接 new 对比)
  4. 3、基本语法和用法
  5. 4、关键特性
  6. 5、总结与建议
  7. 二、示例代码
  8. 1、示例代码 1
  9. 2、示例代码 2

更多推荐文章

查看全部
  • React Native 自定义弹窗警告框
  • Android Framework 核心源码解析与移动开发技术趋势
  • GitHub 启用双因素身份验证(2FA)配置指南
  • C++ 哈希表核心概念与实现原理
  • Faster Whisper 语音识别高效实现与使用指南
  • Python 微信小程序校园二手商品交易系统设计与实现
  • 基于 Windows 环境搭建 OpenClaw 部署指南
  • C++分布式系统负载均衡实战与高并发流量调度
  • 从零实现 FPGA 256 点 FFT:Verilog 手写蝶形运算与资源优化实战
  • Android ClassLoader 核心机制与分类解析
  • 从 Python 到 OpenClaw:构建本地 AI 助理的实战路径
  • 语义化 AI 驱动器:提示词工程的技术演进与治理
  • Web 端 3D 开发的实用技术栈梳理
  • GitHub Copilot 集成安全风险与应对策略
  • 基于 Brython 与 LocalStorage 实现本地记仇本应用
  • JavaScript 数据类型全解析:分类、判断与内存差异
  • JDK 17 新特性整理
  • DeepSeek-R1-Distill-Llama-8B Python 爬虫实战:数据采集与清洗
  • Python 七大就业方向详解:从 Web 开发到人工智能
  • Windows 11 环境下 Python 3.12.5 安装与配置实战

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online