std:: unique_ptr
|
定义于头文件
<memory>
|
||
|
template
<
class
T,
|
(1) | (C++11 起) |
|
template
<
class
T,
|
(2) | (C++11 起) |
std::unique_ptr
是一种智能指针,它通过指针拥有(负责)并管理另一个对象,并在
unique_ptr
超出作用域时处置该对象。
当以下任一情况发生时,将使用关联的删除器处置该对象:
对象通过调用
get_deleter
(
)
(
ptr
)
被销毁,该操作使用可能由用户提供的删除器。默认删除器(
std::default_delete
)使用
delete
运算符,该运算符会销毁对象并释放内存。
一个
unique_ptr
也可以不拥有任何对象,这种情况下它被称为
空
的。
有两种版本的
unique_ptr
:
- 管理单个对象(例如使用 new 分配)。
- 管理动态分配的对象数组(例如使用 new [ ] 分配)。
该类满足 MoveConstructible 与 MoveAssignable 的要求,但不满足 CopyConstructible 与 CopyAssignable 的要求。
如果
T*
不是有效类型(例如
T
是引用类型),则实例化
std
::
unique_ptr
<
T, Deleter
>
定义的程序将是非良构的。
| 类型要求 | ||
-
Deleter
必须是
函数对象
或对
函数对象
的左值引用或对函数的左值引用,且可调用参数类型为
unique_ptr
<
T, Deleter
>
::
pointer
。
|
目录 |
注释
只有非 const 的
unique_ptr
能够将其管理的对象所有权转移给另一个
unique_ptr
。若对象的生命周期由
const
std
::
unique_ptr
管理,则该生命周期被限制在创建该指针的作用域内。
unique_ptr
通常用于管理对象的生命周期,包括:
- 为处理具有动态生存期对象的类和函数提供异常安全性,保证在正常退出和异常退出时都能执行删除操作。
- 将具有动态生命周期的唯一拥有对象的所有权传递给函数。
- 从函数中获取具有动态生命周期的唯一拥有对象的所有权。
- 作为移动感知容器中的元素类型,例如 std::vector ,这些容器持有指向动态分配对象的指针(例如需要多态行为时)。
unique_ptr
可以针对
不完整类型
T
进行构造,例如用于在
pImpl 惯用法
中作为句柄使用。如果使用默认删除器,则
T
必须在调用删除器的代码位置是完整类型,这发生在
unique_ptr
的析构函数、移动赋值运算符和
reset
成员函数中。(相比之下,
std::shared_ptr
无法从指向不完整类型的原始指针构造,但可以在
T
不完整时被销毁)。请注意,如果
T
是类模板特化,将
unique_ptr
用作操作数(例如
!
p
)时,由于
ADL
的要求,需要
T
的模板参数为完整类型。
如果
T
是某个基类
B
的
派生类
,那么
unique_ptr
<
T
>
可以
隐式转换
为
unique_ptr
<
B
>
。转换后得到的
unique_ptr
<
B
>
的默认删除器将使用针对
B
的
operator delete
,这会导致
未定义行为
,除非
B
的析构函数是
虚函数
。请注意
std::shared_ptr
的行为有所不同:
std::
shared_ptr
<
B
>
将使用针对类型
T
的
operator delete
,即使
B
的析构函数不是
虚函数
,所拥有的对象也能被正确删除。
与
std::shared_ptr
不同,
unique_ptr
可以通过任何满足
NullablePointer
要求的自定义句柄类型来管理对象。例如,通过提供定义了
typedef
boost::offset_ptr
pointer;
的
Deleter
或其他
fancy pointer
,可以实现对位于共享内存中对象的管理。
| 功能测试 宏 | 值 | 标准 | 功能 |
|---|---|---|---|
__cpp_lib_constexpr_memory
|
202202L
|
(C++23) |
constexpr
std::unique_ptr
|
嵌套类型
| 类型 | 定义 |
| pointer |
std::
remove_reference
<
Deleter
>
::
type
::
pointer
若该类型存在,否则为
T*
。必须满足
NullablePointer
|
| element_type |
T
,由此
unique_ptr
管理的对象类型
|
| deleter_type |
Deleter
,函数对象或函数/函数对象的左值引用,将在析构函数中被调用
|
成员函数
构造新的
unique_ptr
(公开成员函数) |
|
|
若存在被管理对象则销毁它
(公开成员函数) |
|
赋值
unique_ptr
(公开成员函数) |
|
修改器 |
|
|
返回指向被管理对象的指针并释放所有权
(公开成员函数) |
|
|
替换被管理对象
(公开成员函数) |
|
|
交换被管理对象
(公开成员函数) |
|
观察器 |
|
|
返回指向被管理对象的指针
(公开成员函数) |
|
|
返回用于销毁被管理对象的删除器
(公开成员函数) |
|
|
检查是否有关联的被管理对象
(公开成员函数) |
|
单对象版本,
|
|
|
解引用指向被管理对象的指针
(公开成员函数) |
|
数组版本,
|
|
|
提供对被管理数组的索引访问
(公开成员函数) |
|
非成员函数
|
(C++14)
(C++20)
|
创建管理新对象的唯一指针
(函数模板) |
|
(C++20 中移除)
(C++20)
|
与另一个
unique_ptr
或与
nullptr
进行比较
(函数模板) |
|
(C++20)
|
将托管指针的值输出到输出流
(函数模板) |
|
(C++11)
|
特化
std::swap
算法
(函数模板) |
辅助类
|
(C++11)
|
std::unique_ptr
的哈希支持
(类模板特化) |
示例
#include <cassert> #include <cstdio> #include <fstream> #include <iostream> #include <locale> #include <memory> #include <stdexcept> // 用于下方运行时多态演示的辅助类 struct B { virtual ~B() = default; virtual void bar() { std::cout << "B::bar\n"; } }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // 消费 unique_ptr 的函数可以通过值或右值引用接收 std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } // 用于自定义删除器演示的辅助函数 void close_file(std::FILE* fp) { std::fclose(fp); } // 基于 unique_ptr 的链表演示 struct List { struct Node { int data; std::unique_ptr<Node> next; }; std::unique_ptr<Node> head; ~List() { // 在循环中顺序销毁链表节点,默认析构函数会递归调用其“next”的析构函数, // 对于足够大的链表会导致栈溢出。 while (head) { auto next = std::move(head->next); head = std::move(next); } } void push(int data) { head = std::unique_ptr<Node>(new Node{data, std::move(head)}); } }; int main() { std::cout << "1) 独占所有权语义演示\n"; { // 创建(独占拥有的)资源 std::unique_ptr<D> p = std::make_unique<D>(); // 将所有权转移给 “pass_through”, // 该函数通过返回值将所有权转移回来 std::unique_ptr<D> q = pass_through(std::move(p)); // “p” 现在处于已移动的“空”状态,等于 nullptr assert(!p); } std::cout << "\n" "2) 运行时多态演示\n"; { // 创建派生资源并通过基类指针指向它 std::unique_ptr<B> p = std::make_unique<D>(); // 动态分派按预期工作 p->bar(); } std::cout << "\n" "3) 自定义删除器演示\n"; std::ofstream("demo.txt") << 'x'; // 准备要读取的文件 { using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>; unique_file_t fp(std::fopen("demo.txt", "r"), &close_file); if (fp) std::cout << char(std::fgetc(fp.get())) << '\n'; } // 在此处调用 “close_file()”(如果 “fp” 不为空) std::cout << "\n" "4) 自定义 lambda 表达式删除器和异常安全演示\n"; try { std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr) {
缺陷报告
下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
| 缺陷报告 | 应用于 | 发布时的行为 | 正确行为 |
|---|---|---|---|
| LWG 4144 | C++11 |
T*
未要求构成有效类型
|
要求构成有效类型 |
参见
|
(C++11)
|
具有共享对象所有权语义的智能指针
(类模板) |
|
(C++11)
|
对由
std::shared_ptr
管理的对象的弱引用
(类模板) |
|
(C++26)
|
包含具有类值语义的动态分配对象的包装器
(类模板) |
|
(C++17)
|
可容纳任何
CopyConstructible
类型实例的对象
(类) |