我们向系统申请资源,使用完之后必须要还给系统。c++中最常使用的资源就是动态分配的内存,你不使用的时候一定要归还给系统,不然就会出现内存泄露。我们一般会选择去手动释放资源,delete 掉申请的内存。但是人往往就会漏掉这些东西,内存管理就会变得头疼,于是我们就引入了更高效的内存管理方法:智能指针。
原理:
我们知道在c++中对象在生命周期结束时会自动调用析构函数。这不正是我们想要的所谓的“智能”。于是我们的策略就是:用对象来管理资源。
把资源放入对象中,就可以依赖c++的析构函数自动调用机制,确保资源的释放。
还有一点很重要的就是智能指针实际上对象,只不过它代理了原始指针的行为,所以我们称为智能指针。
分类:
智能指针有很多种,这里我们主要介绍boost.smart_ptr库中的几种智能指针:shared_ptr,weak_ptr,scoped_ptr,scoped_array,shared_array。
这些指针都是轻量级的对象,都是异常安全的。
为了使用这些组件,我们在头文件中要加入:
#include <boost/smart_ptr.hpp>
using namespace boost;
scoped_ptr的用法
scoped_ptr的类摘要:
template <class T>
class scoped_ptr{
private:
T * px; //原始指针
scoped_ptr(scoped_ptr const &); //复制构造函数
scoped_ptr & operator= (scoped_ptr const &); //赋值运算符
public:
explicit scoped_ptr(T * p = 0); //构造函数
explicit scoped_ptr(std::auto_ptr<T> p); //构造函数
~scoped_ptr(); //析构函数
void reset(T * p =0); //重置智能指针
T & operator*(); //重载指针操作符
T & operator->();
T & get() const; //获取原始指针
operator bool() const; //bool值装换
void swap(scoped_ptr & b); //交换指针
};
从构造函数说起,scoped_ptr的构造函数接受一个类型为T*的指针p(它是来自于new表达式动态分配的资源或者是空指针),然后创建一个scoped_ptr对象,并在内部保存指针参数p。当scoped_ptr对象的生命周期结束时,析构函数~scoped_ptr()会调用delete自动销毁指针对象,回收资源。
复制构造函数和赋值运算符重载被声明为私有,说明不能对智能指针进行复制操作。这也说明了scoped_ptr的所有权是不可转移的。
scoped_ptr重载了引用操作符*和箭头操作符->,模仿原始指针的行为,所以我们可以把scoped_ptr对象当做指针来使用。
scoped_ptr的用法:
scoped_ptr<string> sp (new string("text"));
cout << * sp << endl; //取字符串内容
cout << sp->size() << endl; //取字符串的长度
scoped_ptr不允许拷贝和赋值,只能进行* 或->操作。
下面是实例:
#include <boost/smart_ptr.hpp>
using namespace boost;
using namespace std;
class Simple{
public:
Simple(const char * file_name){
cout << "open file:" << file_name << endl;
}
~Simple(){
cout << "close file" << endl;
}
};
int main(void){
scoped_ptr<int> p (new int); //int指针的scoped_ptr
if(p){
* p = 100;
cout << * p << endl;
}
p.reset(); //置空scoped_ptr
assert(p == 0);
if(!p){ //在bool语境中测试
cout << "scoped_ptr== null" << endl;
}
scoped_ptr<Simple> fp(new Simple("test.txt"));
}
scoped_array的用法:
它和scoped_ptr很类似,只不过是scoped_array包装了new[]在堆上分配的动态数组,当然析构的时候要使用delete[]。
scoped_array<int> sa (new int [100]);
sa[10] = 10;
*sa = 20; //错误用法
scoped_array只提供了operator[]操作符,没有提供指针运算。
建议:
在需要使用动态数组的情况下,我们应该使用vector,它比scoped_array更方便。
shared_ptr的用法:
终于来到最关键的地方,它是最常用,也是最有用的智能指针。它是引用计数型的智能指针。
shared_ptr与scoped_ptr区别:
shared_ptr可以被安全共享,可以拷贝,赋值,比较。
shared_ptr有多种构造函数形式。
1.无参的 shared_ptr()创建一个持有空指针的shared_ptr。
2.shared_ptr( Y * p)获得指针p的管理权,同时引用计数置为1。
3.shared_ptr(shared_ptr const & r)从另外一个shared_ptr获取指针的管理权,同时引用计数加1,结果是两个shared_ptr共享一个指针的管理权。
4.shared_ptr(auto_ptr & r) 从一个auto_ptr获得指针的管理权,同时auto_ptr自动失去管理权。
5.shared_ptr(Y * p, D d)行为类型shared_ptr( Y * p),但是参数d指定了析构时的定制删除器,而不是简单的delete。
shared_ptr 的reset()函数作用时将引用计数减1,停止对指针的共享。
shared_ptr有两个专门的函数检查引用计数。unique()在shared_ptr是指针的唯一所有者时返回true。use_count()返回当前指针的引用计数。
shared_ptr的使用:
shared_ptr<int> sp(new int(10));
assert(sp.unique()); //现在shared_ptr是指针的唯一持有者
shared_ptr<int > sp2 = sp; //调用复制构造函数
assert(sp.use_count() ==2) //引用计数为2
工厂函数的使用:
shared_ptr很好的消除了显示delete调用,但是在shared_ptr构造中还需要显示调用new,这样就导致代码不对称性(强迫症)。shared_ptr提供了一个自由的工厂函数make_shared(),来显示消除new调用。
实例:
int main(void){
shared_ptr<string> sp = make_shared<string> ("make_shared"); //创建string共享指针。
shared_ptr<vector<int>> spv = make_shared<vector<int>>(10,2); //创建vector的共享指针
shared_array的使用:
它和shared_ptr类似,包装了new[]操作符在堆上分配的动态数组,为动态数组提供了一个代理。
实例:
#include <boost/smart_ptr.hpp>
using namespace boost;
int main(void){
int * p = new int [100];
shared_array<int> sa(p); //shared_array代理动态数组
shared_array<int> sa2 = sa; //共享所有权
sa[10] = 10; //使用operator[]访问元素
assert(sa2[0] == 10);
weak_ptr的使用:
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它像一个助手协助shared_ptr,像旁观者来观测资源的使用情况。weak_ptr可以从一个shared_ptr或者另一个weak_ptr构造,但是weak_ptr没有共享资源,它的构造和析构不会影响引用计数的变化。
成员函数:
-use_count()观测引用计数。
-expired()检测是否失效。
-lock()获得一个可用的shared_ptr对象,从而操作资源。
实例:
shared_ptr <int> sp (new int (10));
assert(sp.use_count()==1);
weak_ptr<int> wp(sp);
assert(wp.use_count() ==1); //weak_ptr不影响引用计数
if(! wp.expired()){
shared_ptr<int> sp2 = wp.lock(); //获得一个shared_ptr
*sp2 = 100;
assert(wp.use_count()==2);
}