一、简介
智能指针的行为是类似于指针的类对象,但这种指针还有其它的用途。本文主要介绍三个可帮助管理动态内存分配的智能指针模板。先来看看需要哪些功能,以及这些功能是怎么实现的。先来看下面的函数:
void model(std::string &str)
{
std::string * ps = new std::string(str);
.......
if(weired_thing())
throw exception();
delete ps;
return;
}
每次调用此函数,都将进行堆内存的分配,但是是否会执行delete语句,这取决于weired_thing()函数,一旦引发异常,delete将不执行,从而造成内存泄露。有没有更好的方法来解决这种问题,现在我们分析一下。当model函数返回时(无论是正常退出,还是异常退出),本地变量都将从栈内存中释放,即ps占据的内存将被释放。如果ps所指向的内存也被释放,那该有多好啊。如果ps有一个析构函数,当ps过期时,该析构函数将会释放ps所指向的内存。因此ps的问题在于,它是一个普通的指针,不是有析构函数的类对象。这正是auto_ptr、unique_ptr和shared_ptr背后的思想。这三个智能指针模板都定义了类似指针的对象,可以将new获得的地址赋给指针对象。当智能指针过期时,其析构函数将使用delete来释放内存,因此将new返回的地址赋给这些对象,将无需记住以后要调用delete来释放这些内存。
二、auto_ptr<T>
下面我们从源码的角度来分析auto_ptr<T>
auto_ptr类包含一个私有数据成员ap。我们先来看第7行的代码 explicit auto_ptr(T *ptr = 0) :ap(ptr){},该构造函数是用ptr的值来初始化私有数据成员,explicit 目的在于防止指针隐式转化为对象,例如:auto_ptr<T> p = new T()//这句是不能通过编译的。该类一个重要的成员函数是第40行的release,该函数的主要作用是释放对象所管理的内存返回一个指针从而完成转移指针的所有权,回头我们在看第8行的复制构造函数,用下面的代码来解释此复制构造函数的工作原理:
auto_ptr<int> p1(new int(1));
auto_ptr<int> p2(p1);//调用复制构造函数
std::cout<<*p2;//输出1
std::cout<<*p1;//运行时错误
执行第1句后,p1.ap将指向一块堆内存;执行完第2句后,程序已将p1所指向的堆内存转移给p2,p1将不再指向那块堆内存,即p2.ap=p1.ap,p1.ap = NULL;第4句对空指针的取内容是非法的,因而会产生运行时错误。也可以用同样的原理来解释赋值构造函数(也转让了所有权),这里不再赘述。
三、unique_ptr<T>
unique_ptr是用来代替auto_ptr的产物,它提供了严格意义上的所有权,包括:
①拥有它指向的对象;
②由于把复制构造函数和赋值构造函数声明为私有的,因此无法进行复制和赋值操作,但是它增加了移动构造和右值引用,请看下面代码:
unique_ptr<int> p1(new int(1));
unique_ptr<int> p2(p1);//编译时报错
p2 = p1;//编译时报错
p2 = std::move(p1);//合法,移动构造
unique_ptr<int> p3;
p3 = unique_ptr<int>(new int(2));//合法,等号的右边为临时的右值
总之,程序试图将一个unique_ptr赋值给另一个时,如果源unique_ptr为一个临时的右值,编译器允许这样做;如果源unique_ptr将存在一段时间,编译器将禁止这样做。
③保存指向一个对象的指针,当本身过期时,将调用指定的删除器来释放所指向的内存。
unique_ptr实现如下功能:
①为申请的动态内存提供异常安全机制
②将动态申请的内存所有权传递给函数
③从某个函数返回动态申请内存的所有权
④在容器中保存指针
⑤auto_ptr应具有的功能
unique_ptr具体应用如下:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
unique_ptr<string> f()
{
return unique_ptr<string>(new string("fun"));
}
int main()
{
unique_ptr<string> ps1(new string("123"));
cout << *ps1 << endl;
unique_ptr<string> ps2 = move(ps1);
if (ps1 == NULL)
cout << "ps1 = NULL" << endl;
cout << *ps2 << endl;
ps2 = unique_ptr<string>(new string("456"));
cout << *ps2 << endl;
ps2 = f();
cout << *ps2 << endl;
string *pstr = ps2.release();
if (ps2 == NULL)
cout << "ps2 = NULL" << endl;
cout << *pstr << endl;
ps2.reset(pstr);
cout << *ps2 << endl;
}
输出为:
四、shared_ptr
shared_ptr是可以共享对象的所有权,它采用的是引用计数的设计策略,即当新增一个shared_ptr对该对象管理时,
该对象的引用计数加1,当减少1个shared_ptr对该对象管理时,该对象的引用计数减1,当该对象的引用计数减为0时
,才调用delete来释放该对象。
shared_ptr具体使用如下:
#include <iostream>
#include <memory>
#include <string>
using namespace std;
int main()
{
shared_ptr<string> ps1(new string("123"));
cout << *ps1 << endl;
shared_ptr<string> ps2 = ps1;//调用复制构造函数,引用计数加1.
cout << "引用计数 = " << ps1.use_count() << endl;
ps2.reset(new string("345"));//重置ps2所管理的对象
cout << *ps2 << endl;
cout << "引用计数 = " << ps1.use_count() << endl;
ps2.swap(ps1);//交换所管理的对象
cout << *ps1 << endl;
cout << *ps2 << endl;
string *str = ps1.get();//返回常规指针
cout << "str = "<<*str << endl;
if(ps1.unique())//检查所管里的对象是否仅有当前shared_ptr实例管理
cout<<"所管里的对象仅有当前shared_ptr实例管理"<<endl;
}
输出结果为: