C++ 11 创建和使用 unique_ptr
2016-06-29 20:46
288 查看
std::unique_ptr实现了独享所有权的语义。一个非空的std::unique_ptr总是拥有它所指向的资源。转移一个std::unique_ptr将会把所有权也从源指针转移给目标指针(源指针被置空)。拷贝一个std::unique_ptr将不被允许,因为如果你拷贝一个std::unique_ptr,那么拷贝结束后,这两个std::unique_ptr都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。因此std::unique_ptr是一个仅能移动(move_only)的类型。当指针析构时,它所拥有的资源也被销毁。默认情况下,资源的析构是伴随着调用std::unique_ptr内部的原始指针的delete操作的。
下图演示了两个unique_ptr实例之间的所有权转换。
1、如何创建unique_ptr
unique_ptr不像shared_ptr一样拥有标准库函数make_shared来创建一个shared_ptr实例。要想创建一个unique_ptr,我们需要将一个new操作符返回的指针传递给unique_ptr的构造函数。示例:
intmain() { //创建一个unique_ptr实例 unique_ptr<int>pInt(newint(5)); cout<<*pInt; }
2、无法进行复制构造和赋值操作
unique_ptr没有copy构造函数,不支持普通的拷贝和赋值操作。intmain() { //创建一个unique_ptr实例 unique_ptr<int>pInt(newint(5)); unique_ptr<int>pInt2(pInt);//报错 unique_ptr<int>pInt3=pInt;//报错 }
3、可以进行移动构造和移动赋值操作
unique_ptr虽然没有支持普通的拷贝和赋值操作,但却提供了一种移动机制来将指针的所有权从一个unique_ptr转移给另一个unique_ptr。如果需要转移所有权,可以使用std::move()函数。示例:
intmain() { unique_ptr<int>pInt(newint(5)); unique_ptr<int>pInt2=std::move(pInt);//转移所有权 //cout<<*pInt<<endl;//出错,pInt为空 cout<<*pInt2<<endl; unique_ptr<int>pInt3(std::move(pInt2)); }
4、可以返回unique_ptr
unique_ptr不支持拷贝操作,但却有一个例外:可以从函数中返回一个unique_ptr。示例:
unique_ptr<int>clone(intp) { unique_ptr<int>pInt(newint(p)); returnpInt;//返回unique_ptr } intmain(){ intp=5; unique_ptr<int>ret=clone(p); cout<<*ret<<endl; }
使用举例: { //创建一个指向int的空指针 std::unique_ptr<int>fPtr1; std::unique_ptr<int>fPtr2(newint(4)); autofPtr3=std::make_unique<int>(); //fPtr2释放指向对象的所有权,并且被置为nullptr std::cout<<"fPtr2releasebefore:"<<fPtr2.get()<<std::endl; int*pF=fPtr2.release(); std::cout<<"fPtr2releasebefore:"<<fPtr2.get()<<"andpFvalue:"<<*pF<<std::endl; //所有权转移,转移后fPtr3变为空指针 std::cout<<"movebeforefPtr1address:"<<fPtr1.get()<<"fPtr3address:"<<fPtr3.get()<<std::endl; fPtr1=std::move(fPtr3); std::cout<<"moveafterfPtr1address:"<<fPtr1.get()<<"fPtr3address:"<<fPtr3.get()<<std::endl; std::cout<<"movebeforefPtr1address:"<<fPtr1.get()<<std::endl; fPtr1.reset(); std::cout<<"moveafterfPtr1address:"<<fPtr1.get()<<std::endl; } 输出: fPtr2releasebefore:00EFB120 fPtr2releasebefore:00000000andpFvalue:4 movebeforefPtr1address:00000000fPtr3address:00EFEC60 moveafterfPtr1address:00EFEC60fPtr3address:00000000 movebeforefPtr1address:00EFEC60 moveafterfPtr1address:00000000
unique_ptr使用场景
1、为动态申请的资源提供异常安全保证
我们先来看看下面这一段代码:voidFunc() { int*p=newint(5); //...(可能会抛出异常) deletep; }
这是我们传统的写法:当我们动态申请内存后,有可能我们接下来的代码由于抛出异常或者提前退出(if语句)而没有执行delete操作。
解决的方法是使用unique_ptr来管理动态内存,只要unique_ptr指针创建成功,其析构函数都会被调用。确保动态资源被释放。
voidFunc() { unique_ptr<int>p(newint(5)); //...(可能会抛出异常) }
2、返回函数内动态申请资源的所有权
unique_ptr<int>Func(intp) { unique_ptr<int>pInt(newint(p)); returnpInt;//返回unique_ptr } intmain(){ intp=5; unique_ptr<int>ret=Func(p); cout<<*ret<<endl; //函数结束后,自动释放资源 }
3、在容器中保存指针
intmain() { vector<unique_ptr<int>>vec; unique_ptr<int>p(newint(5)); vec.push_back(std::move(p));//使用移动语义 }
4、管理动态数组
标准库提供了一个可以管理动态数组的unique_ptr版本。intmain() { unique_ptr<int[]>p(newint[5]{1,2,3,4,5}); p[0]=0;//重载了operator[] }
5、作为auto_ptr的替代品
创建与释放举例#include<iostream> #include<memory> #include<stdlib.h> structFoo { Foo(){std::cout<<"Foo::Foo\n";} ~Foo(){std::cout<<"Foo::~Foo\n";} voidbar(){std::cout<<"Foo::bar\n";} }; voidf(constFoo&) { std::cout<<"f(constFoo&)\n"; } structD { voidoperator()(Foo*foo) { std::cout<<"Doperator()"<<std::endl; deletefoo; } }; voidTestAutoDestroy() { //1.普通的new对象. std::cout<<"TestDestroy...................."<<std::endl; { std::unique_ptr<Foo>p1(newFoo); } //2.普通的new[]对象. { std::unique_ptr<Foo[]>p2(newFoo[4]); } //3.自定义的deleter. { std::unique_ptr<Foo,D>p3(newFoo); } } voidTestOwner() { std::cout<<"TestOwner...................."<<std::endl; //1.newobject. std::unique_ptr<Foo>p1(newFoo);//p1ownsFoo if(p1)p1->bar(); { std::unique_ptr<Foo>p2(std::move(p1));//nowp2ownsFoo f(*p2); p1=std::move(p2);//ownershipreturnstop1 p2->bar(); std::cout<<"destroyingp2...\n"; } p1->bar(); } voidTestArrayOwner() { std::cout<<"TestArrayOwner...................."<<std::endl; //1.new[]object. std::unique_ptr<Foo[]>p1(newFoo[4]);//p1ownsFoo if(p1)p1[0].bar(); { std::unique_ptr<Foo[]>p2(std::move(p1));//nowp2ownsFoo f(p2[0]); p1=std::move(p2);//ownershipreturnstop1 p2[0].bar(); std::cout<<"destroyingp2...\n"; } p1[0].bar(); } intmain() { TestAutoDestroy(); TestOwner(); TestArrayOwner(); } 输出:
TestDestroy.................... Foo::Foo Foo::~Foo Foo::Foo Foo::Foo Foo::Foo Foo::Foo Foo::~Foo Foo::~Foo Foo::~Foo Foo::~Foo Foo::Foo Doperator() Foo::~Foo TestOwner.................... Foo::Foo Foo::bar f(constFoo&) Foo::bar destroyingp2... Foo::bar Foo::~Foo TestArrayOwner.................... Foo::Foo Foo::Foo Foo::Foo Foo::Foo Foo::bar f(constFoo&) Foo::bar destroyingp2... Foo::bar Foo::~Foo Foo::~Foo Foo::~Foo Foo::~Foo
相关文章推荐
- C++容器的insert()函数有以下三种用法: 最终*it=val;
- 栈练习之C语言中实现中缀转后缀表达式
- 图片处理(四)之边沿检测
- 传统高斯模糊与优化算法(附完整C++代码)
- c++ new高级用法
- C++中substr函数的用法
- C语言内存浅谈
- c++动态联编和静态联编
- c++虚函数
- c++纯虚函数
- c++虚基类
- c++多重继承
- c++多态性
- 树莓派GPIO控制--C语言篇
- ubuntu系统vim基本配置
- C++错误累积
- #define a int[10] typedef int a[10]
- 大家好,这是我第一篇在CSDN上的博文,本篇主要讨论小型内存池的设计
- C++ 11 创建和使用 shared_ptr
- C++经典书籍推荐