unique_ptr
std::unique_ptr 是 C++11 引入的智能指针类型,用于管理动态分配的对象。它的核心特点是:独占所有权,不能被复制,只能被移动。
它通过 RAII(资源获取即初始化)机制在对象生命周期结束时自动释放资源,能显著减少 new / delete 手动管理导致的内存泄漏风险。
头文件与基本特征
使用 unique_ptr 时,通常包含:
#include <memory>
主要特征:
| 特征 | 说明 |
|---|---|
| 所有权模型 | 独占所有权(同一时刻只有一个 unique_ptr 拥有对象) |
| 拷贝语义 | 禁止拷贝 |
| 移动语义 | 支持移动(std::move) |
| 释放时机 | 指针析构时自动释放 |
| 自定义删除器 | 支持 |
基本用法
推荐使用 std::make_unique(C++14 起可用)创建对象:
#include <iostream>
#include <memory>
int main() {
std::unique_ptr<int> p = std::make_unique<int>(42);
std::cout << *p << '\n';
return 0;
}
如果你严格处于 C++11 环境,没有 make_unique,可临时写成:
std::unique_ptr<int> p(new int(42));
交互演示(MDX + React)
下面这个交互面板用于演示 move、reset、release 的状态变化。它不是执行真实 C++ 代码,而是可视化 unique_ptr 所有权规则。
std::unique_ptr 所有权演示
通过点击按钮观察 move / reset / release 的状态变化。
ptrA 拥有对象
最近操作
- 初始化: ptrA -> Resource#1
不能拷贝,只能移动
unique_ptr 不允许拷贝:
std::unique_ptr<int> p1(new int(10));
// std::unique_ptr<int> p2 = p1; // 编译错误
如果要转移所有权,必须移动:
#include <memory>
int main() {
std::unique_ptr<int> p1(new int(10));
std::unique_ptr<int> p2 = std::move(p1);
// 现在 p1 == nullptr, p2 拥有对象
return 0;
}
移动后,源指针通常变为空指针,应先判断再解引用。
常用成员函数
| 函数 | 作用 |
|---|---|
get() | 获取裸指针(不转移所有权) |
release() | 放弃所有权并返回裸指针(需要手动释放) |
reset(ptr) | 释放当前对象并接管新对象 |
reset() | 释放当前对象并置空 |
swap(other) | 与另一个 unique_ptr 交换所有权 |
operator* / operator-> | 像普通指针一样访问对象 |
operator bool | 判断是否为空 |
get()
get() 只借出裸指针,不改变所有权:
std::unique_ptr<int> p(new int(5));
int* raw = p.get();
raw 不能被 delete,因为资源仍由 p 管理。
release()
release() 会交出所有权:
std::unique_ptr<int> p(new int(5));
int* raw = p.release();
// p 变为空,raw 需要手动 delete
delete raw;
reset()
reset() 用于替换或清空托管对象:
std::unique_ptr<int> p(new int(5));
p.reset(new int(9)); // 先释放 5,再接管 9
p.reset(); // 释放 9 并置空
作为函数参数与返回值
作为参数(转移所有权)
#include <memory>
void consume(std::unique_ptr<int> p) {
// p 在函数结束时自动释放
}
int main() {
std::unique_ptr<int> p(new int(7));
consume(std::move(p));
return 0;
}
作为返回值
#include <memory>
std::unique_ptr<int> createValue() {
return std::unique_ptr<int>(new int(99));
}
int main() {
std::unique_ptr<int> p = createValue();
return 0;
}
返回 unique_ptr 是工厂函数的常见写法,表达“创建者把所有权交给调用者”。
管理数组
unique_ptr 可以管理动态数组:
#include <memory>
int main() {
std::unique_ptr<int[]> arr(new int[3]);
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
return 0;
}
数组版本使用 unique_ptr<T[]>,它会调用 delete[]。
自定义删除器
当资源不是用 delete 释放时,可以提供删除器:
#include <cstdio>
#include <memory>
struct FileCloser {
void operator()(FILE* file) const {
if (file) {
std::fclose(file);
}
}
};
int main() {
std::unique_ptr<FILE, FileCloser> fp(std::fopen("data.txt", "w"));
if (fp) {
std::fputs("hello\n", fp.get());
}
return 0;
}
这让 unique_ptr 不仅能管内存,也能管文件句柄、套接字等资源。
使用注意
- 不要拷贝
unique_ptr,只能std::move。 - 移动后源指针通常为空,不要直接解引用。
get()只是观察,不转移所有权。- 调用了
release()就要负责后续释放,否则会泄漏。 - 尽量避免直接
new,优先make_unique(可用时)。 - 需要共享所有权时,用
std::shared_ptr,不要硬把unique_ptr复制给多个地方。
小结
std::unique_ptr 是现代 C++ 里最基础也最重要的资源管理工具之一。它把“谁负责释放资源”这个问题变成编译期可检查的所有权模型,在保证性能的同时显著降低内存泄漏风险。掌握移动语义、reset、release 和函数间传递模式后,基本就能覆盖多数日常使用场景。