C++11中的智能指针
在C++11中有四种只能指针,auto_ptr,shared-ptr,unique_ptr和weak-ptr。其中auto_ptr有和多不足之处,在C++11中已经建议废弃使用。
shared_ptr类
智能指针也是模板。所以创建智能指针时要提供其指向的指针类型:
shared_ptr
shared_ptr>p2 //
指向int的list,在C++11中>
中的>>再也不用空格了
在给shared_ptr分配内存时建议使用make_shared函数,这样最安全。它与智能指针的头文件相同都位于memory头文件中。
//指向一个值为42的int的shared_ptr
shared_ptr
//指向一个值为”9999999999”的string
shared_ptr
//p5指向一个值初始化的int,即值为0
shared_ptr
make_shared函数中的参数可以是其指向类型中拥有的构造函数的参数一样。
当对shared_ptr进行拷贝和赋值操作每个shared_ptr都会记录它所指向对象的个数,一般称之为引用计数。当进行拷贝操作时他们所指向的对象的引用计数都会增加,一旦一个shared_ptr的引用计数变为0,它就会自动释放自己所管理的对象。
autor=make_shared
r=q;//r原来对象的引用计数会减1,而其新指向的对象的引用计数会加1.
当shared_ptr的引用计数变为0,它就会调用所指向类型的析构函数来释放内存,但注意是调用delete来释放的,当其指向数组时要手动指定释放函数。
智能指针在异常发生时也能释放其拥有的内存。
void f()
{
int*p=new int(42);
//在这段代码之间发生了一异常
deletep;
}
如果在new和delete之间发生一场且异常没有在f中捕获,则内存永远不会释放了。
假设为们正在使用一个C和C++的网络库,有如下代码:
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_ptr
//若我们在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_ptr
unique_ptr
unique_ptr
p3=p2; //错误,不支持赋值操作
我们可有通过release和reset方法将指针的所有权从一个(非const)unique_ptr转移给另一个unique_ptr
unique_ptr
unique_ptr
p2.reset(p3.release());
注意release()并不会释放内存只会转交拥有权,reset()可以释放内存。
不能拷贝unique_ptr的规则有一个例外,我们可以拷贝或复制一个将要被销毁的unique_ptr。最常见的就是从函数返回一个unique_ptr:
unique_ptr
{
//正确
return unique_ptr
}
还可以返回一个局部对象的拷贝:
unique_ptr
{
//正确
unique_ptr
…
return ret;
}
这种机制的实现是使用的是C++11的新语法std::move(),它可以“窃取”资源而不是拷贝资源。具体可以参考std::move()相关知识
unique_ptr中的删除器
类似shared_ptr,unique_ptr默认情况下用delete释放它指向的对象。在构建时必须提供删除器的类型。
unique_ptr
用unique_ptr代替上面连接程序
void f(destination &d)
{
connection c=connection(&d);
unique_ptr
//若我们在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
weak_ptr
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock().此函数检查weak_ptr指向的对象是否存在。如果存在lock则返回一个指向共享对象的shared_ptr,同时该对象的引用计数会增加
if(shared_ptr
{
}
智能指针和动态数组
标准库提供了一个可以管理new分配数组的unique_ptr版本,为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对方括号。
unique_ptr
up.realese();//自动用delete[]销毁其指针。
当unique_ptr指向数组时我们不能使用点和箭头运算符,而是用下标来访问数组中的元素:
for(size_ti=0;i I != 10; ++i)
up[i]=i;
unique_ptr
unique_ptr
u[i] //返回位置i的元素
与unique_ptr不同,shared_ptr不直接支持管理共带数组,如果希望使用shared_ptr管理一个数组必须提供一个自定义的删除器:
share_ptr
sp.reset();
for(size_ti=0;i I != 10; ++i)
*(sp.get()+i)=i;//由于shared_ptr不直接支持动态数组所以我们不能用下标直接访问元素。