C++智能指针 unique_ptr
概念
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。
unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。
unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
基本操作
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> uptr;
uptr.reset(new int(3));
cout << *uptr << endl;
int* ptr = uptr.release();
// cout << *uptr << endl;
// error. uptr = nullptr
cout << *ptr << endl;
uptr.reset(new int(4));
unique_ptr<int> ano_uptr(new int(5));
uptr.swap(ano_uptr);
cout << *uptr << endl; // *uptr = 5
cout << *ano_uptr << endl; // *ano_uptr = 4
delete ptr;
return 0;
}
使用场景
1. 动态资源的异常保护
详情见前文资源管理对把分配资源放入容器中的讨论。
2. 返回函数内创建的资源
由于在函数中创建资源是非常容易导致内存泄露的。因为客户可能会忘记哪个指针指向了on the heap的资源。
unique_ptr<X> foo()
{
unique_ptr<X> px(new X);
// do something
return px; //移动语义
}
3. 可放入容器中
弥补了auto_ptr不能放入容器中的问题。同时unique_ptr并无copy语义,只能用move转移语义。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
int main() {
vector< unique_ptr<string> > vec_of_uptr;
vec_of_uptr.push_back(unique_ptr<string>(new string("yan")));
vec_of_uptr.push_back(unique_ptr<string>(new string("ze")));
vec_of_uptr.push_back(unique_ptr<string>(new string("xin")));
unique_ptr<string> new_one(new string("new"));
vec_of_uptr.push_back(move(new_one));
return 0;
}
4. 动态管理数组
unique_ptr<int[]> array_ptr(new int [3]{1, 2, 3});
array_ptr[0] = 2;
5. 自定义资源删除操作
unique_prt的部分源码。(使用了成员模板)
template <class _Up, class _Ep>
unique_ptr(unique_ptr<_Up, _Ep>&);
unique_ptr默认的资源删除操作是delete/delete[],若需要,可以进行自定义:在 资源管理 介绍shared_ptr时也有提及这部分内容。简单的说,就是我们析构资源不一定是delete,可能是要我们自定义的函数。
void end_connection(connection *p) { disconnect(*p); } //资源清理函数
unique_ptr<connection, decltype(end_connection)*> //资源清理器的“类型”
p(&c, end_connection);// 传入函数名,会自动转换为函数指针
6. auto_ptr与unique_ptr
在C++11环境下,auto_ptr被看做“遗留的”,他们有如下区别:
- auto_ptr有拷贝语义,拷贝后源对象变得无效;unique_ptr则无拷贝语义,但提供了移动语义
- auto_ptr不可作为容器元素,unique_ptr可以作为容器元素
- auto_ptr不可指向动态数组(尽管不会报错,但不会表现出正确行为),unique_ptr可以指向动态数组