C++ 11智能指针之unique_ptr
2015-10-20 18:08
369 查看
unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
1、拥有它所指向的对象。
2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。
3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。
使用unique_ptr,可以实现以下功能,包括:
1、为动态申请的内存提供异常安全。
2、将动态申请内存的所有权传递给某个函数。
3、从某个函数返回动态申请内存的所有权。
4、在容器中保存指针。
5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。
下面是一段传统的会产生不安全异常的代码:
==================================================================================================================
智能指针是基于RAII机制实现的类(模板),具有指针的行为(重载了operator*与operator->操作符),可以“智能”地销毁其所指对象。C++11中有unique_ptr、shared_ptr与weak_ptr等智能指针,可以对动态资源进行管理
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。
unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。
unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
[cpp] view
plaincopy
//智能指针的创建
unique_ptr<int> u_i; //创建<span style="font-family:Arial,Helvetica,sans-serif">“</span><span style="font-family:Arial,Helvetica,sans-serif">空智能指针”</span>
u_i.reset(new int(3)); //"绑定”动态对象
unique_ptr<int> u_i2(new int(4));//创建时指定动态对象
//所有权的变化
int *p_i = u_i2.release(); //释放所有权
unique_ptr<string> u_s(new string("abc"));
unique_ptr<string> u_s2 = std::move(u_s); //所有权转移(通过移动语义),u_s所有权转移后,变成“空指针”
u_s2=nullptr;//显式销毁所指对象,同时智能指针变为空指针。与u_s2.reset()等价
(1) 动态资源的异常安全保证(利用其RAII特性):
[cpp] view
plaincopy
void foo()
{//不安全的代码
X *px = new X;
// do something, exception may occurs
delete px; // may not go here
}
[cpp] view
plaincopy
void foo()
{//异常安全的代码。无论是否异常发生,只要px指针成功创建,其析构函数都会被调用,确保动态资源被释放
unique_ptr<X> px(new X);
// do something,
}
(2) 返回函数内创建的动态资源
[cpp] view
plaincopy
unique_ptr<X> foo()
{
unique_ptr<X> px(new X);
// do something
return px; //移动语义
}
(3) 可放在容器中(弥补了auto_ptr不能作为容器元素的缺点)
方式一:
[cpp] view
plaincopy
vector<unique_ptr<string>> vs { new string{“Doug”}, new string{“Adams”} };
方式二:
[cpp] view
plaincopy
vector<unique_ptr<string>>v;
unique_ptr<string> p1(new string("abc"));
v.push_back(std::move(p1));//这里需要显式的移动语义,因为unique_ptr并无copy语义
(4) 管理动态数组,因为unique_ptr有unique_ptr<X[]>重载版本,销毁动态对象时调用delete[]
[cpp] view
plaincopy
unique_ptr<int[]> p (new int[3]{1,2,3});
p[0] = 0;// 重载了operator[]
unique_ptr默认的资源删除操作是delete/delete[],若需要,可以进行自定义:
[cpp] view
plaincopy
void end_connection(connection *p) { disconnect(*p); } //资源清理函数
unique_ptr<connection, decltype(end_connection)*> //资源清理器的“类型”
p(&c, end_connection);// 传入函数名,会自动转换为函数指针
在C++11环境下,auto_ptr被看做“遗留的”,他们有如下区别:
auto_ptr有拷贝语义,拷贝后源对象变得无效;unique_ptr则无拷贝语义,但提供了移动语义
auto_ptr不可作为容器元素,unique_ptr可以作为容器元素
auto_ptr不可指向动态数组(尽管不会报错,但不会表现出正确行为),unique_ptr可以指向动态数组
==============================================================================================================================
我们知道auto_ptr通过复制构造或者通过=赋值后,原来的auto_ptr对象就报废了.所有权转移到新的对象中去了.而通过shared_ptr可以让多个智能指针对象同时拥有某一块内存的访问权.但假如我们不希望多个内存块被多个智能指针对象共享,同时又不会像auto_ptr那样不知不觉的就让原来的auto_ptr对象失效,可咋整呢?
这个时候就要使用unique_ptr了,顾名思义嘛,unique是唯一的意思.说明它跟auto_ptr有点类似,就是同时只能有一个智能指针对象指向某块内存.但它还有些其他特性.
比如auto_ptr<int> ap(new int(88 );
auto_ptr<int> one (ap) ; // ok
auto_ptr<int> two = one; //ok
但unique_ptr不支持上述操作
unique_ptr<int> ap(new int(88 );
unique_ptr<int> one (ap) ; // 会出错
unique_ptr<int> two = one; //会出错
就是像上面这样一般意义上的复制构造和赋值或出错.但在函数中作为返回值却可以用.
unique_ptr<int> GetVal( ){
unique_ptr<int> up(new int(88 );
return up;
}
unique_ptr<int> uPtr = GetVal(); //ok
实际上上面的的操作有点类似于如下操作
unique_ptr<int> up(new int(88 );
unique_ptr<int> uPtr2 = std:move( up) ; //这里是显式的所有权转移. 把up所指的内存转给uPtr2了,而up不再拥有该内存.另外注意如果你使用vs2008是没有std:move这函数的.
//vs2010开始才有,是c++ 11标准出现的内容.
我们知道auto_ptr不可做为容器元素.而unique_ptr也同样不能直接做为容器元素,但可以通过一点间接的手段
例如:
unique_ptr<int> sp(new int(88) );
vector<unique_ptr<int> > vec;
vec.push_back(std::move(sp));
//vec.push_back( sp ); 这样不行,会报错的.
//cout<<*sp<<endl;但这个也同样出错,说明sp添加到容器中之后,它自身报废了.
从上面的例子可以看出,unique_ptr和auto_ptr真的非常类似.其实你可以这样简单的理解,auto_ptr是可以说你随便赋值,但赋值完了之后原来的对象就不知不觉的报废.搞得你莫名其妙.而unique就干脆不让你可以随便去复制,赋值.如果实在想传个值就哪里,显式的说明内存转移std:move一下.然后这样传值完了之后,之前的对象也同样报废了.只不过整个move你让明显的知道这样操作后会导致之前的unique_ptr对象失效.
==================================================================================================================================
unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
1、拥有它所指向的对象。
2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。
3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。
使用unique_ptr,可以实现以下功能,包括:
1、为动态申请的内存提供异常安全。
2、将动态申请内存的所有权传递给某个函数。
3、从某个函数返回动态申请内存的所有权。
4、在容器中保存指针。
5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。
下面是一段传统的会产生不安全异常的代码:
解决方法是,使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。
如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。
现在,我们可以这样使用函数f():
unique_ptr具有移动语义,所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。
1、拥有它所指向的对象。
2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。
3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。
使用unique_ptr,可以实现以下功能,包括:
1、为动态申请的内存提供异常安全。
2、将动态申请内存的所有权传递给某个函数。
3、从某个函数返回动态申请内存的所有权。
4、在容器中保存指针。
5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。
下面是一段传统的会产生不安全异常的代码:
X* f() { X* p = new X; // 做一些事情,可能会抛出某个异常 return p; }解决方法是,使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。
X* f() { unique_ptr<X> p(new X); // 做一些事情,可能会抛出异常 return p.release(); }如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。
unique_ptr<X> f() { unique_ptr<X> p(new X); // 做一些事情,可能会抛出异常 return p; }现在,我们可以这样使用函数f():
void g() { unique_ptr<X> q = f(); // 使用移动构造函数(move constructor) q->DoSomething(); // 使用q X x = *q; // 复制指针q所指向的对象 } // 在函数退出的时候,q以及它所指向的对象都被删除释放unique_ptr具有移动语义,所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。
==================================================================================================================
1. 智能指针概念
智能指针是基于RAII机制实现的类(模板),具有指针的行为(重载了operator*与operator->操作符),可以“智能”地销毁其所指对象。C++11中有unique_ptr、shared_ptr与weak_ptr等智能指针,可以对动态资源进行管理
2. unique_ptr概念
unique_ptr“唯一”拥有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。unique_ptr指针本身的生命周期:从unique_ptr指针创建时开始,直到离开作用域。离开作用域时,若其指向对象,则将其所指对象销毁(默认使用delete操作符,用户可指定其他操作)。
unique_ptr指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过reset方法重新指定、通过release方法释放所有权、通过移动语义转移所有权。
3. unique_ptr的基本操作:
[cpp] viewplaincopy
//智能指针的创建
unique_ptr<int> u_i; //创建<span style="font-family:Arial,Helvetica,sans-serif">“</span><span style="font-family:Arial,Helvetica,sans-serif">空智能指针”</span>
u_i.reset(new int(3)); //"绑定”动态对象
unique_ptr<int> u_i2(new int(4));//创建时指定动态对象
//所有权的变化
int *p_i = u_i2.release(); //释放所有权
unique_ptr<string> u_s(new string("abc"));
unique_ptr<string> u_s2 = std::move(u_s); //所有权转移(通过移动语义),u_s所有权转移后,变成“空指针”
u_s2=nullptr;//显式销毁所指对象,同时智能指针变为空指针。与u_s2.reset()等价
4. unique_ptr的使用场景
(1) 动态资源的异常安全保证(利用其RAII特性):[cpp] view
plaincopy
void foo()
{//不安全的代码
X *px = new X;
// do something, exception may occurs
delete px; // may not go here
}
[cpp] view
plaincopy
void foo()
{//异常安全的代码。无论是否异常发生,只要px指针成功创建,其析构函数都会被调用,确保动态资源被释放
unique_ptr<X> px(new X);
// do something,
}
(2) 返回函数内创建的动态资源
[cpp] view
plaincopy
unique_ptr<X> foo()
{
unique_ptr<X> px(new X);
// do something
return px; //移动语义
}
(3) 可放在容器中(弥补了auto_ptr不能作为容器元素的缺点)
方式一:
[cpp] view
plaincopy
vector<unique_ptr<string>> vs { new string{“Doug”}, new string{“Adams”} };
方式二:
[cpp] view
plaincopy
vector<unique_ptr<string>>v;
unique_ptr<string> p1(new string("abc"));
v.push_back(std::move(p1));//这里需要显式的移动语义,因为unique_ptr并无copy语义
(4) 管理动态数组,因为unique_ptr有unique_ptr<X[]>重载版本,销毁动态对象时调用delete[]
[cpp] view
plaincopy
unique_ptr<int[]> p (new int[3]{1,2,3});
p[0] = 0;// 重载了operator[]
5. 自定义资源删除操作(Deleter):
unique_ptr默认的资源删除操作是delete/delete[],若需要,可以进行自定义:[cpp] view
plaincopy
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可以指向动态数组
==============================================================================================================================
我们知道auto_ptr通过复制构造或者通过=赋值后,原来的auto_ptr对象就报废了.所有权转移到新的对象中去了.而通过shared_ptr可以让多个智能指针对象同时拥有某一块内存的访问权.但假如我们不希望多个内存块被多个智能指针对象共享,同时又不会像auto_ptr那样不知不觉的就让原来的auto_ptr对象失效,可咋整呢?
这个时候就要使用unique_ptr了,顾名思义嘛,unique是唯一的意思.说明它跟auto_ptr有点类似,就是同时只能有一个智能指针对象指向某块内存.但它还有些其他特性.
1.无法进行复制构造与赋值操作.
比如auto_ptr<int> ap(new int(88 );auto_ptr<int> one (ap) ; // ok
auto_ptr<int> two = one; //ok
但unique_ptr不支持上述操作
unique_ptr<int> ap(new int(88 );
unique_ptr<int> one (ap) ; // 会出错
unique_ptr<int> two = one; //会出错
2.可以进行移动构造和移动赋值操作
就是像上面这样一般意义上的复制构造和赋值或出错.但在函数中作为返回值却可以用.unique_ptr<int> GetVal( ){
unique_ptr<int> up(new int(88 );
return up;
}
unique_ptr<int> uPtr = GetVal(); //ok
实际上上面的的操作有点类似于如下操作
unique_ptr<int> up(new int(88 );
unique_ptr<int> uPtr2 = std:move( up) ; //这里是显式的所有权转移. 把up所指的内存转给uPtr2了,而up不再拥有该内存.另外注意如果你使用vs2008是没有std:move这函数的.
//vs2010开始才有,是c++ 11标准出现的内容.
3.可做为容器元素
我们知道auto_ptr不可做为容器元素.而unique_ptr也同样不能直接做为容器元素,但可以通过一点间接的手段例如:
unique_ptr<int> sp(new int(88) );
vector<unique_ptr<int> > vec;
vec.push_back(std::move(sp));
//vec.push_back( sp ); 这样不行,会报错的.
//cout<<*sp<<endl;但这个也同样出错,说明sp添加到容器中之后,它自身报废了.
总结:
从上面的例子可以看出,unique_ptr和auto_ptr真的非常类似.其实你可以这样简单的理解,auto_ptr是可以说你随便赋值,但赋值完了之后原来的对象就不知不觉的报废.搞得你莫名其妙.而unique就干脆不让你可以随便去复制,赋值.如果实在想传个值就哪里,显式的说明内存转移std:move一下.然后这样传值完了之后,之前的对象也同样报废了.只不过整个move你让明显的知道这样操作后会导致之前的unique_ptr对象失效.==================================================================================================================================
unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
1、拥有它所指向的对象。
2、无法进行复制构造,也无法进行复制赋值操作。也就是说,我们无法得到指向同一个对象的两个unique_ptr。但是可以进行移动构造和移动赋值操作。
3、保存指向某个对象的指针,当它本身被删除释放的时候(比如,离开了某个作用域),会使用给定的删除器释放它指向的对象。
使用unique_ptr,可以实现以下功能,包括:
1、为动态申请的内存提供异常安全。
2、将动态申请内存的所有权传递给某个函数。
3、从某个函数返回动态申请内存的所有权。
4、在容器中保存指针。
5、所有auto_ptr应该具有的(但无法在C++ 03中实现的)功能。
下面是一段传统的会产生不安全异常的代码:
1 X* f() 2 { 3 X* p = new X; 4 // 做一些事情,可能会抛出某个异常 5 return p; 6 }
解决方法是,使用unique_ptr来管理这个对象的所有权,由其进行这个对象的释放工作。
1 X* f() 2 { 3 unique_ptr<X> p(new X); 4 // 做一些事情,可能会抛出异常 5 return p.release(); 6 }
如果程序执行过程中抛出了异常,unique_ptr就会释放它所指向的对象。但是,除非我们真的需要返回一个内建的指针,我们还可以返回一个unique_ptr。
1 unique_ptr<X> f() 2 { 3 unique_ptr<X> p(new X); 4 // 做一些事情,可能会抛出异常 5 return p; 6 }
现在,我们可以这样使用函数f():
1 void g() 2 { 3 unique_ptr<X> q = f(); // 使用移动构造函数(move constructor) 4 q->DoSomething(); // 使用q 5 X x = *q; // 复制指针q所指向的对象 6 } // 在函数退出的时候,q以及它所指向的对象都被删除释放
unique_ptr具有移动语义,所以我们可以使用函数f()返回的右值对q进行初始化,这样就简单地将所有权传递给了q。
相关文章推荐
- C++ 11智能指针之shared_ptr
- 设计模式之单例模式
- 怎么理解【前台PHP,中间件用Java,底层用C/C++】
- C++ 11 bind
- c语言学习笔记(15) 数组基础
- C语言(8) 变量在内存中的分配原则
- C语言(7) 模块化编程及多文件开发注意事项
- C语言(4) 原码、反码、补码与位运算
- C++ 11 function
- C语言(6) unix命令/C手动编译/include指令/Xcode 清空缓存的方法
- C++ 11 tuple & 可变参数模板
- C语言(5) 函数使用需要注意的地方
- 《C++ Primer 5th edition》 第一章 开始
- C++ 11语法甜点2
- c语言函数指针的定义
- 【好玩的棋盘游戏】编写一个玩家操作的函数和电脑能够操作的两人棋盘游戏,哪边有三个相连的胜出
- C语言--字符串处理函数的使用
- VC++ 关机代码
- C++ 11语法甜点1
- 【经典游戏编程题】C语言:编写一个棋盘游戏,与二维数组结合