1、什么是智能指针
智能指针是行为类似于指针的类对象。
2、为什么使用智能指针
看一下函数:
void remodel(std::string& str)
{
std::string* ps = new std::string(str);
...
str = ps;
return;
}
可以看出代码中存在错误,分配的内存没有收回。解决的办法是在return前面加上一句:
delete ps;
但是实际工作当中难免忘记,因此智能指针就是基于这样一种思想:给予ps一个析构函数,让ps过期时自动地释放指向的内存。
3、使用智能指针
C++有四种智能指针:auto_ptr、unique_ptr、shared_ptr和weak_ptr。本文主要介绍前三种。
要创建智能指针,首先要包含头文件memory,该文件定义模版。例如,auto_ptr包含如下构造函数:
template<class X>class auto_ptr
{
public:
explicit auto_ptr(X* p = 0)throw();
}
auto_ptr<double>pd(new double);
上述语句声明了一个指向double的智能指针。
如果将2中的普通指针用智能指针替换。如下:
void remodel(std::string& str)
{
std::auto_ptr<std::string> ps(new std::string(str));
...
str = *ps;
return;
}
注意:
(1)智能指针模版位于命名空间std内。
(2)所有的智能指针类都有一个explicit构造函数,将指针作为参数,因此不需要自动将指针转换为智能指针。
例如:
shared_ptr<doubel>pd;
double* p=new double;
pd=p;//not allowed
pd=shared_ptr<double>(p);//allowed
4、三种智能指针的区别
智能指针在很多方面类似于常规指针:解引用(*ps)、访问结构成员(ps->data)、将它赋给常规类型的指针,但是某些情况下可能出现问题。
(1)首先用个避免一点:
string v("Hello,world!");
shared_ptr<string>pv(&v);
当pv过期时会使用delete来释放v的非堆内存,这是错误的。
(2)看下面代码:
auto_ptr<string> ps(new string("Hello,world!"));
auto_ptr<string> ps1;
ps1=ps;
如果ps和ps1为常规指针,那么ps和ps1指向同一个string对象,但是对于智能指针这是无法接受的,因为程序会释放一个对象两次。为此可以使用三种方法解决:
1)定义赋值运算符,执行深度复制,使指针指向不同的对象。
2)建立所有权概念,即对一个对象只能有一个智能指针拥有它,使用赋值操作转让所有权。auto_ptr和unique_ptr采用这个方案,但是unique_ptr的策略更严格。
auto_ptr<string> ps(new string("Hello,world!"));
auto_ptr<string> ps1;
ps1=ps;//allowed
ps1接管string对象的所有权后,ps的所有权被剥夺。这样可以防止ps和ps1的析构函数同时删除同一个对象。但是如果随后使用ps则会出现问题。
但是使用unique_ptr编译器则会报错:
unique_ptr<string> ps(new string("Hello,world!"));
unique_ptr<string> ps1;
ps1=ps;//not allowed
在将一个unique_ptr赋给另一个时,如果源unique_ptr为临时右值,编译器允许赋值;如果源unique_ptr将存在一段时间则不允许这么做。例如:
unique_ptr<string> ps(new string("Hello,world!"));
unique_ptr<string> ps1;
ps1=ps;//not allowed
ps1=unique_ptr<string>(new string("Hello,world1"));//allowed
总之,unique_ptr的安全性要比auto_ptr高。
3)创建更高智能的指针,跟踪引用特定对象的智能指针数,称为引用计数。赋值时计数加1,指针过期时计数减1,只有最后一个指针过期时才调用delete。shared_ptr采用这个方案。
5、选择智能指针
如果程序要使用多个指向同一对象的指针,应选择shared_ptr;
如果不需要多个指向同一对象的指针,则选择unique_ptr。