智能指针: unique_ptr
2023年12月28日 2023年12月30日
独占所管理的动态对象:
- 拷贝构造函数为删除
delete
- 拷贝赋值运算符为删除
delete
移动构造函数和移动赋值运算符存在:
- 可以使用一个unique_ptr右值初始化另一个unique_ptr
- 可以使用一个unique_ptr右值为另一个unique_ptr赋值
创建unique_ptr: 不支持使用一个unique_ptr左值去初始化另一个unique_ptr
1unique_ptr<string> p1(new string("Stegosaurus")); 2unique_ptr<string> p2(p1); // 错误: p1是左值
赋值运算符
右侧运算对象可以是nullptr
如果unique_ptr不为空:
- 销毁unique_ptr所指动态对象
- 将unique_ptr置空
1u = nullptr;
右侧运算对象不能是另一个unique_ptr左值
1unique_ptr<string> p1(new string("Stegosaurus")); 2unique_ptr<string> p2; 3p2 = p1; // 错误: p1是左值
使用unique_ptr接管右值资源
移动构造函数接受一个unique_ptr右值
1unique_ptr<T> up((unique_ptr<T>(new T)));
移动赋值运算符的右操作数可以是一个unique_ptr右值
1auto p = new T; 2unique_ptr<T> up; 3up = unique_ptr<T>(p);
示例
1#include <iostream> 2#include <ostream> 3#include <string> 4#include <vector> 5#include <memory> 6 7using namespace std; 8 9class Foo 10{ 11 friend ostream &operator<<(ostream &os, const Foo &f); 12 13public: 14 Foo(string _name) : name(_name) {} 15 ~Foo() { cout << "Call ~Foo for " << *this << endl << endl; } 16 17private: 18 string name; 19}; 20 21ostream &operator<<(ostream &os, const Foo &f) 22{ 23 os << f.name; 24 return os; 25} 26 27void DeleteFoo(Foo *f) 28{ 29 cout << "call " << __FUNCTION__ << " for " << *f << endl; 30 delete f; // 调用~Foo() 31} 32 33int main() 34{ 35 auto p1 = new Foo("Kate"); 36 auto p2 = new Foo("Peter"); 37 38 unique_ptr<Foo> up(p1); 39 40 up = unique_ptr<Foo>(p2); // 为Kate调用析构函数 41 42 cout << "main finished.\n"; 43 return 0; 44} // main函数结束才为Peter调用析构函数
输出
Call ~Foo for Kate main finished. Call ~Foo for Peter
release操作: 交接动态对象的管理权限
如果unique_ptr不为空
- 返回指向动态对象的内置指针
- 将unique_ptr置空
1u.release();
要妥善处理release返回的动态对象
示例
- 用来初始化另一个unique_ptr
1unique_ptr<string> p1(new string("Stegosaurus")); 2unique_ptr<string> p2(p1.release()); // 将所有权从p1移交给p2;p1变为空指针
- 销毁动态对象
1unique_ptr<string> p(new string("Stegosaurus")); 2delete p.release(); // 直接销毁p管理的动态对象
reset操作: 接管内置指针管理的动态对象
提供重载版本
- 形参列表为空, 或者传入nullptr
如果unique_ptr非空, 销毁所指动态对象, 将其置空 - 接受一个内置指针
- 如果unique_ptr非空, 销毁所指动态对象, 将其置空
- 如果内置指针非空, unique_ptr接管内置指针所指动态对象
- 如果unique_ptr非空, 销毁所指动态对象, 将其置空
1up.reset(); 2up.reset(nullptr); 3 4up.reset(p);
示例: 接管release操作返回的动态对象
1unique_ptr<string> p1(new string("Stegosaurus")); 2unique_ptr<string> p2(new string("Trex")); 3p1.reset(p2.release()); // 如果p1非空, 销毁所指动态对象; p1接管release操作返回的动态对象; p2亦被置为空, 由release保证 4 // 释放p1管理的资源, 将p2所指动态对象的所有权移交给p1; 将p2置空: 这一系列操作实则等价于unique_ptr之间的赋值操作
返回一个unique_ptr
允许拷贝一个将要销毁的unique_ptr
允许使用一个将要销毁的unique_ptr为另一个unique_ptr赋值
-
返回一个unique_ptr
1unique_ptr<int> clone(int p) 2{ 3 return unique_ptr<int>(new int(p)); 4}
-
返回局部对象的拷贝
1uniuqe_ptr<int> clone(int p) 2{ 3 unique_ptr<int> ret(new int(p)); 4 return ret; 5}
保证了unique_ptr所管理动态资源仍有效
注意: 使用一个unique_ptr初始化shared_ptr
P412提到了该操作
1shared_ptr<T> p(u);
-
不能使用一个unique_ptr左值初始化shared_ptr
1auto p = new Foo("Peter"); 2unique_ptr<Foo> up(p); 3 4shared_ptr<Foo> sp(up); // 报错: no known conversion from 'unique_ptr<Foo>' to 'std::nullptr_t'
-
可以使用一个unique_ptr右值初始化一个shared_ptr
会将unique_ptr置空1#include <iostream> 2#include <ostream> 3#include <string> 4#include <vector> 5#include <memory> 6 7using namespace std; 8 9class Foo 10{ 11 friend ostream &operator<<(ostream &os, const Foo &f); 12 13public: 14 Foo(string _name) : name(_name) {} 15 ~Foo() { cout << "Call ~Foo for " << *this << endl << endl; } 16 17private: 18 string name; 19}; 20 21ostream &operator<<(ostream &os, const Foo &f) 22{ 23 os << f.name; 24 return os; 25} 26 27void DeleteFoo(Foo *f) 28{ 29 cout << "call " << __FUNCTION__ << " for " << *f << endl; 30 delete f; // 调用~Foo() 31} 32 33int main() 34{ 35 auto p = new Foo("Peter"); 36 37 unique_ptr<Foo> up(p); 38 cout << "*up equals " << *up << endl; 39 40 shared_ptr<Foo> sp1(std::move(up)); 41 42 if (up == nullptr) cout << "is null.\n"; 43 44 cout << "main finished.\n"; 45 return 0; 46}
输出
*up equals Peter is null. main finished. Call ~Foo for Peter
unique_ptr与删除器
删除器是一个可调用对象 deleter
需在定义unique_ptr对象时给出删除器类型
默认初始化
给出删除器类型
1unique_ptr<T, D> up;
直接初始化
- 绑定删除器
- 使用内置指针初始化, 绑定删除器
1unique_ptr<T, D> up(d); 2 3unique_ptr<T, D> up2(new T, d);
使用删除器管理非动态对象
1void end_connection(connection *p) { disconnect(*p); } 2 3void f(destination &d) 4{ 5 connection c = connect(&d); 6 unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection); 7}