智能指针引入的目的是为了解决悬挂指针的问题,关于悬挂指针,相信很多人都碰到过。对类中包含指针的类,在进行拷贝构造或者赋值操作时要尤其小心,但是往往很难避开悬挂指针的问题。
智能指针的实现是通过引用计数来完成的。将一个指针对象和引用计数关联起来,使用引用计数来追踪对象的使用。
①创建新对象时,初始化指针,并设置引用计数为1;
②当对象作为另外一个对象的副本创建,也就是调用拷贝构造函数时,拷贝指针,并且,增加引用计数;
③当对一个对象进行赋值时,左操作数所指对象的引用计数减少,如果减少为0,则删除对象,右操作数所指对象的引用计数增加;
④调用析构函数,减少引用计数,如果减至0,则删除指针。
引用计数的实现由两种经典策略:引入辅助类和使用句柄。分两篇内容详细说明。
首先介绍引入辅助类的方法。
先上一张图,从这个图中可以看出大概的思路。
使用U_Ptr作为辅助类,其实是一个模板类,封装实际的指针对象,和引用计数值。在使用时,不会直接使用这个类,由HasPtr使用。HasPtr是一个模板类,作为对外使用的类,构造时传入实际的指针对象。内部包含了一个辅助类U_Ptr的指针对象,多个HasPtr类对象指向同一个U_Ptr对象,U_Ptr依靠引用计数来实现实际指针对象的释放。
//模板类作为友元类,要事先声明
template <class T>
class HasPtr;
//辅助类
template<typename T>
class U_Ptr {
friend class HasPtr<T>; //友元类
T *ip; //实际指针对象
size_t use; //引用计数
U_Ptr(T *p)
:ip(p), use(1)
{ }
~U_Ptr()
{
delete ip;
}
};
template<typename T>
class HasPtr
{
public:
// 构造函数,引用计数初始化为1
explicit HasPtr(T *p)
:ptr(new U_Ptr<T>(p))
{ }
// 拷贝构造函数,引用计数加1
HasPtr(const HasPtr<T> &orig)
:ptr(orig.ptr)
{
++ptr->use;
}
//赋值
HasPtr<T>& operator=(const HasPtr<T>& rhs)
{
++rhs.ptr->use; // 操作符右值自加
if (--ptr->use == 0) //左值自减,并判断是否减至0
delete ptr; // 减至0则删除
ptr = rhs.ptr; // 拷贝指针
return *this;
}
// 析构,自减
~HasPtr()
{
if (--ptr->use == 0)
delete ptr;
}
//其他操作符,如->、*等略去
private:
U_Ptr<T> *ptr; // 辅助类对象
};