您的位置:首页 > 编程语言 > C语言/C++

C++ 11 创建和使用 unique_ptr

2016-06-29 20:46 288 查看
unique_ptr不共享它的指针。它无法复制到其他unique_ptr,无法通过值传递到函数,也无法用于需要副本的任何标准模板库(STL)算法。只能移动unique_ptr。这意味着,内存资源所有权将转移到另一unique_ptr,并且原始unique_ptr不再拥有此资源。我们建议你将对象限制为由一个所有者所有,因为多个所有权会使程序逻辑变得复杂。因此,当需要智能指针用于纯C++对象时,可使用unique_ptr,而当构造unique_ptr时,可使用make_uniqueHelper函数。

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


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: