C++ 智能指针shared-ptr,unique_ptr和weak-ptr

C++11中的智能指针

C++11中有四种只能指针,auto_ptrshared-ptrunique_ptrweak-ptr。其中auto_ptr有和多不足之处,在C++11中已经建议废弃使用。

shared_ptr

智能指针也是模板。所以创建智能指针时要提供其指向的指针类型:

shared_ptrp1; //指向string

shared_ptr>p2 //指向intlist,在C++11>中的>>再也不用空格了


在给shared_ptr分配内存时建议使用make_shared函数,这样最安全。它与智能指针的头文件相同都位于memory头文件中。


//指向一个值为42intshared_ptr

shared_ptrp3=make_shared(42);

//指向一个值为”9999999999”string

shared_ptrp4=make_shared(10,' 9');

//p5指向一个值初始化的int,即值为0

shared_ptrp5=make_shared();


make_shared函数中的参数可以是其指向类型中拥有的构造函数的参数一样。

当对shared_ptr进行拷贝和赋值操作每个shared_ptr都会记录它所指向对象的个数,一般称之为引用计数。当进行拷贝操作时他们所指向的对象的引用计数都会增加,一旦一个shared_ptr的引用计数变为0,它就会自动释放自己所管理的对象。

autor=make_shared(42);

r=q;//r原来对象的引用计数会减1,而其新指向的对象的引用计数会加1.

shared_ptr的引用计数变为0,它就会调用所指向类型的析构函数来释放内存,但注意是调用delete来释放的,当其指向数组时要手动指定释放函数。

智能指针在异常发生时也能释放其拥有的内存。

void f()

{

int*p=new int(42);

//在这段代码之间发生了一异常

deletep;

}

如果在newdelete之间发生一场且异常没有在f中捕获,则内存永远不会释放了。

假设为们正在使用一个CC++的网络库,有如下代码:

struct destination;

struct connection;

connection connect(destination *);

void disconnection(connection);

void f(destination &d)

{

connection c=connection(&d);

//若我们在f退出前忘记使用disconnection,就无法关闭c

}

可以采用一下方法来解决这种问题

默认shared_ptr是使用delete来释放其所只想对象的内存的,我们可以自定义一个函数来代替delete,这个删除器(deleter)函数必须能够完成对shared_ptr中保存的指针进行释放操作。在上述例子中删除其必须接受单个类型为connection*的参数:

voidedn_connection(connection *p){disconnection(*p);}

当我们创建一个shared_ptr可以传递一个指向删除器的参数

voidf(destination &d)

{

connectionc=connection(&d);

shared_ptrp(&c,end_connection);

//若我们在f退出时(即使是由于异常而退出),connection会被正确关闭

}

使用智能指针时应注意一下5点:

1.不使用内置指针初始化(或reset)多个只能指针。

2.deleteget()返回的指针。

3.不使用get()初始化或reset另一个智能指针

4.如果使用get()返回指针,记住最后一个对应的指针销毁后,你的指针就便为无效了。

5.如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器


unique_ptr

一个unique_ptr“拥有”它所指向的对象,shared_ptr不同,某个时刻一个只能有一个unique_ptr指向一个给定的对象,unique_ptr被销毁时,其所指向的对象也被销毁了。

由于一个unique_ptr拥有它指向的对象,所以unique_ptr不支持普通的拷贝或赋值操作:

unique_ptrp1(new string(“steven”));

unique_ptrp2(p1); //错误,不支持拷贝操作

unique_ptrp3;

p3=p2; //错误,不支持赋值操作

我们可有通过releasereset方法将指针的所有权从一个(const)unique_ptr转移给另一个unique_ptr

unique_ptrp2(p1.release()); //p1放弃所有权,转交给p2

unique_ptrp3(new string(“Trex”));

p2.reset(p3.release());

注意release()并不会释放内存只会转交拥有权,reset()可以释放内存。


不能拷贝unique_ptr的规则有一个例外,我们可以拷贝或复制一个将要被销毁的unique_ptr。最常见的就是从函数返回一个unique_ptr:

unique_ptrclone(int p)

{

//正确

return unique_ptr (new int(p));

}

还可以返回一个局部对象的拷贝:

unique_ptrclone(int p)

{

//正确

unique_ptrret(new int(p));

return ret;

}

这种机制的实现是使用的是C++11的新语法std::move(),它可以“窃取”资源而不是拷贝资源。具体可以参考std::move()相关知识


unique_ptr中的删除器

类似shared_ptrunique_ptr默认情况下用delete释放它指向的对象。在构建时必须提供删除器的类型。

unique_ptrp (new objT,fcn);

unique_ptr代替上面连接程序

void f(destination &d)

{

connection c=connection(&d);

unique_ptrp(&c,end_connection);

//若我们在f退出时(即使是由于异常而退出),connection会被正确关闭

}



weak_ptr

weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个shared_ptr管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象还是会被释放。

当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它:

auto p=make_shared_ptr(42);

weak_ptrwp(p); //wp弱共享pp的引用计数未改变

由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock().此函数检查weak_ptr指向的对象是否存在。如果存在lock则返回一个指向共享对象的shared_ptr,同时该对象的引用计数会增加

if(shared_ptr np= wp.lock())

{

}


智能指针和动态数组

标准库提供了一个可以管理new分配数组的unique_ptr版本,为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对方括号。

unique_ptrup(new int[10]);

up.realese();//自动用delete[]销毁其指针。

unique_ptr指向数组时我们不能使用点和箭头运算符,而是用下标来访问数组中的元素:

for(size_ti=0;i I != 10; ++i)

up[i]=i;


unique_ptru //u指向一个类型为T的动态分配的数组

unique_ptru (p) //u指向内置指针p所指向的动态分配的数组。P必须能转换为类型T

u[i] //返回位置i的元素



unique_ptr不同,shared_ptr不直接支持管理共带数组,如果希望使用shared_ptr管理一个数组必须提供一个自定义的删除器:

share_ptrsp(new int[10] , [](int *p){delete[] p;}); //这里使用了lambda表达式

sp.reset();

for(size_ti=0;i I != 10; ++i)

*(sp.get()+i)=i;//由于shared_ptr不直接支持动态数组所以我们不能用下标直接访问元素。


你可能感兴趣的:(C/C++内存管理,C++11)