智能指針引入的目的是爲了解決懸掛指針的問題,關於懸掛指針,相信很多人都碰到過。對類中包含指針的類,在進行拷貝構造或者賦值操作時要尤其小心,但是往往很難避開懸掛指針的問題。
智能指針的實現是通過引用計數來完成的。將一個指針對象和引用計數關聯起來,使用引用計數來追蹤對象的使用。
①創建新對象時,初始化指針,並設置引用計數爲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; // 輔助類對象
};