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
也可以不占有对象,该情况下称它为空。
std::unique_ptr
有两个版本:
- 管理单个对象(例如以 new 分配)
- 管理动态分配的对象数组(例如以 new[] 分配)
此类满足可移动构造(MoveConstructible) 和可移动赋值(MoveAssignable) ,但不满足可复制构造(CopyConstructible) 或可复制赋值(CopyAssignable) 。
如果 T*
不是合法类型(例如 T
是引用类型),那么实例化 std::unique_ptr<T, Deleter> 的程序非良构。
类型要求 | ||
-Deleter 必须是函数对象(FunctionObject) 或函数对象(FunctionObject) 的左值引用或函数的左值引用,且可以以 unique_ptr<T, Deleter>::pointer 类型的实参调用。 |
目录 |
[编辑]注解
只有非 const 的 unique_ptr
能转移被管理对象的所有权给另一 unique_ptr
。如果对象的生存期由 const std::unique_ptr 所管理,那么它被限定在创建指针的作用域中。
unique_ptr
常用于管理对象的生存期,包含:
- 通过保证在正常退出和经由异常退出两者上都进行删除,向处理拥有动态生存期的对象的类和函数提供异常安全性。
- 向函数传递独占的拥有动态生存期的对象的所有权。
- 从函数获得独占的拥有动态生存期的对象的所有权。
- 作为具移动容器的元素类型,例如保有指向动态分配对象的指针的 std::vector(比如当需要多态行为的场合)。
std::unique_ptr
可为不完整类型T
构造,例如用于改善用作 pImpl 手法中把柄的用途。如果使用默认删除器,那么 T
必须在代码中调用删除器点处完整,这会在析构函数、移动赋值运算符和 unique_ptr
的 reset
成员函数中发生。(与之相反,std::shared_ptr 不能从指向不完整类型的裸指针构造,但可在 T
不完整处销毁)。注意如果 T
是类模板特化,那么把 unique_ptr
用作操作数(如 !p),将因实参依赖查找而要求 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
来管理位于共享内存的对象。
功能特性测试宏 | 值 | 标准 | 功能特性 |
---|---|---|---|
__cpp_lib_constexpr_memory | 202202L | (C++23) | constexprstd::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) | 创建管理一个新对象的独占指针 (函数模板) |
与另一个 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; virtualvoid 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” 现在是已被移动的“空”状态,等于 nullptrassert(!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){std::cout<<"由自定义删除器销毁...\n"; delete ptr;}); throwstd::runtime_error("");// “p” 是普通指针的情况下此处就会泄漏}catch(conststd::exception&){std::cout<<"捕获到异常\n";} std::cout<<"\n""5) 数组形式的 unique_ptr 演示\n";{ std::unique_ptr<D[]> p(new D[3]);}// “D::~D()” 被调用 3 次 std::cout<<"\n""6) 链表演示\n";{ List wall;constint enough{1'000'000};for(int beer =0; beer != enough;++beer) wall.push(beer); std::cout.imbue(std::locale("en_US.UTF-8"));std::cout<<"墙上有 "<< enough <<" 瓶啤酒...\n";}// 销毁所有啤酒}
可能的输出:
1) 独占所有权语义演示 D::D D::bar D::~D 2) 运行时多态演示 D::D D::bar D::~D 3) 自定义删除器演示 x 4) 自定义 lambda 表达式删除器和异常安全性演示 D::D 由自定义删除器销毁... D::~D 捕获到异常 5) 数组形式的 unique_ptr 演示 D::D D::D D::D D::~D D::~D D::~D 6) 链表演示 墙上有 1,000,000 瓶啤酒...
[编辑]缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 4144 | C++11 | T* 不需要组成合法类型 | 需要 |
[编辑]参阅
(C++11) | 拥有共享对象所有权语义的智能指针 (类模板) |
(C++11) | 到 std::shared_ptr 所管理对象的弱引用 (类模板) |
(C++26) | 包含动态分配对象的具有类似值语义的包装器 (类模板) |
(C++17) | 可保有任何可复制构造(CopyConstructible) 类型的实例的对象。 (类) |