摘要
正文
1、智能指针的出现
void remodel(std::string & ps)
{
string * str = new string(ps);
if (weird_thing())
throw exception();
ps = *str;
delete str;
return;
}
在这段代码中 ,,要是(函数weird_thring())返回值为true的话,,,会直接抛出异常;;;到最后 、、、我们动态开辟的string对象str,,,,可能就没有被释放 、、、这就会造成内存的泄露 。。。。
2、智能指针的简单介绍
//auto_ptr实现的智能指针 其中的 不涉及的深拷贝 ,,,,有缺陷
template<class T>
class auto_ptr
{
public:
//定义成显式的构造函数,,, 用防止类型的隐式转换
explicit auto_ptr(T* ptr = NULL)
:_ptr(ptr)
{}
~auto_ptr()
{
if(_ptr)
{
delete _ptr;
}
_ptr = NULL;
}
T& operator*(){};
T* operator->(){};
protected:
T* _ptr;
};
auto_ptr的实现是在C++98提出来的,,,C++11已经将只摈弃了 ,,,但是这种 智能指针还是很有必要的。。。
//相对于auto_ptr来说的话 ,,,,增加了 一个变量来表示指针的合法性
//一个智能指针真正代表的只有一个 ,,,,那就是最新定义,,,或者赋值的那个 对象
//但是还是存在的是 深浅拷贝的问题
template<class T>
class unique_ptr
{
public:
explicit unique_ptr(T* ptr =NULL)
:_ptr(ptr)
,_owner(true)
{}
unique_ptr(unique_ptr<T> & ap)
:_ptr(ap._ptr)
,_owner(true)//将最新拷贝定义的对象 设置为 true
{
if(ap._owner == true)
{
ap._owner = false;//旧的对象 设置为 false
}
else
{
_ptr = NULL;
_owner = false;
}
}
unique_ptr<T> & operator=(const unique_ptr<T> & ap)
{
if(ap._owner == true&&_ptr != ap._ptr)
{
if(_ptr && _owner == true)
{
delete _ptr;
}
_ptr = ap._ptr;
_owner = true;//新的设置成 true
ap._owner =false;
}
return *this;
}
~unique_ptr()
{
if(_ptr && _owner == true)
{
delete _ptr;
}
_ptr =NULL;
_owner =false;
}
protected:
T* _ptr;
bool _owner;//记录当前指针的合法
};
相对于 auto_ptr来说的话 ,,,,unique_ptr 有了 一定的优化 ,,
//对于上面的智能指针产生的问题 ,,,都是由于的是 拷贝还有 赋值引发的深浅拷贝问题
//导致的是 对象析构是的指针释放的问题 。。。。。
//所以就又产生了 一个新的指针,,,那就是 scoped_ptr
//用来 防止拷贝与 赋值
template<class T>
class scoped_ptr
{
public:
explicit scoped_ptr(const T* ptr = NULL)
:_ptr(ptr)
{}
~scoped_ptr()
{
if(_ptr)
{
delete _ptr;
}
_ptr =NULL;
}
protected:
//将拷贝与赋值定义成私有的,,,就能防止拷贝
scoped_ptr(const scoped_ptr<T> & ap);
scoped_ptr<T>& operator=(const scoped_ptr<T> & ap);
protected:
T* _ptr;
};
template<class T>
class shared_ptr
{
public:
explicit shared_ptr(T* ptr =NULL)
:_ptr(ptr)
,_count(NULL)
{
if(_ptr)
_count = new int(1);
}
shared_ptr(const shared_ptr<T>& sp)
:_ptr(sp._ptr)
,_count(sp._count)
{
if(_ptr)
{
(*_count)++;
}
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp )
{
if(_ptr!= sp._ptr)
{
if(*_count == 1)
{
delete _ptr;
delete _count;
}
_ptr = sp._ptr;
_count = sp._count;
if(_ptr)
{
(*_count)++;
}
}
return *this;
}
~shared_ptr()
{
if(_ptr)
{
if(*_count == 1)
{
delete _ptr;
delete _count;
}
else
{
(*_count )--;
}
}
_ptr =NULL;
_count =NULL;
}
protected:
T* _ptr;
int * _count;
};
3、boost库对智能指针的优化实现
在boost库中 ,,,使用一个弱引用智能指针(weak_ptr)来打破循环引用(weak_ptr不增加引用计数)
weak_ptr是一种 不控制的所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象 。将一个weak_ptr
绑定到一个shared_ptr上,不会改变shared_ptr的引用计数。一旦 最后一个指向对象的shared_ptr被销毁的话 ,对象就会被释放,即使weak_ptr有指向的对象,,对象也还是会被释放 。
因此,weak_ptr就是抓住了这种智能指针 "弱" 共享对象的特点。。。
2、定制删除器
我们都知道 ,,,有些指针在 它的作用域结束的话 ,就要进行 对它进行 合适的处理。。
但是这样的指针,不仅仅只有动态开辟的空间指针。。。例如:
就像是 文件指针,,我们在文件操作结束之后,,需要关闭文件 ,,,调用 fclose()函数 。。。
对于这样的指针,,我们实现的指针指针没有办法 ,,对它来进行 尾处理
所以,就产生了有了这个定制的删除器问题 。。。
在boost库中为我们设计了这样的函数,,,我们要做的只是 设计一个定制的删除器的仿函数 。。。
#include <boost/shared_ptr.hpp>
using namespace boost;
// 定置的删除器仿函数
class FClose
{
public :
void operator () (void* ptr)
{
cout<<"fclose" <<endl;
fclose((FILE *)ptr);
}
};
class Free
{
public :
void operator () (void* ptr)
{
cout<<"free" <<endl;
free(ptr );
}
};
void Test ()
{
// 定制删除器
shared_ptr<FILE > p1( fopen("test.txt" , "w"), FClose());
// 定制删除器和分配器
shared_ptr<int > p2((int *)malloc( sizeof(int )), Free(), allocator<int >
());
}
4、分析在不同的场合使用什么类型的智能指针
在掌握了这几种智能指针后,大家可能会想另一个问题:在实际应用中,应使用哪种智能指针呢?
下面给出几个使用指南。
(1)如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。这样的情况包括:
- 有一个指针数组,并使用一些辅助指针来标示特定的元素,如最大的元素和最小的元素;
- 两个对象包含都指向第三个对象的指针;
- STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出warning)和auto_ptr(行为不确定)。如果你的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。
(2)如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。如果函数使用new分配内存,并返还指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器在那个,只要不调用将一个unique_ptr复制或赋给另一个算法(如sort())。例如,可在程序中使用类似于下面的代码段
unique_ptr<int> make_int(int n)
{
return unique_ptr<int>(new int(n));
}
void show(unique_ptr<int> &p1)
{
cout << *a << ' ';
}
int main()
{
...
vector<unique_ptr<int> > vp(size);
for(int i = 0; i < vp.size(); i++)
vp[i] = make_int(rand() % 1000); // copy temporary unique_ptr
vp.push_back(make_int(rand() % 1000)); // ok because arg is temporary
for_each(vp.begin(), vp.end(), show); // use for_each()
...
}
其中push_back调用没有问题,因为它返回一个临时unique_ptr,该unique_ptr被赋给vp中的一个unique_ptr。另外,如果按值而不是按引用给show()传递对象,for_each()将非法,因为这将导致使用一个来自vp的非临时unique_ptr初始化pi,而这是不允许的。前面说过,编译器将发现错误使用unique_ptr的企图。
在unique_ptr为右值时,可将其赋给shared_ptr,这与将一个unique_ptr赋给一个需要满足的条件相同。与前面一样,在下面的代码中,make_int()的返回类型为unique_ptr<int>:
unique_ptr<int> pup(make_int(rand() % 1000)); // ok
shared_ptr<int> spp(pup); // not allowed, pup as lvalue
shared_ptr<int> spr(make_int(rand() % 1000)); // ok
模板shared_ptr包含一个显式构造函数,可用于将右值unique_ptr转换为shared_ptr。shared_ptr将接管原来归unique_ptr所有的对象。
在满足unique_ptr要求的条件时,也可使用auto_ptr,但unique_ptr是更好的选择。如果你的编译器没有unique_ptr,可考虑使用Boost库提供的scoped_ptr,它与unique_ptr类似。