標籤:引用計數指標
如前面記憶體泄露中所講的,引用計數指標可以被複製。因此,一個智能指標的幾份拷貝可以指向同一個對象。這就產生了由哪份拷貝負責刪除它們共同指向的對象這個問題。答案是這組智能指標中最後消亡的那個將刪除它所指向的對象。這類似於家居規則:“最後一個離開屋子的人負責關燈。”為了實現這個演算法,這些指標共用一個計數器,記錄有多少個智能指標引用同一個對象,即“引用計數”這個術語的由來。引用計數的應用範圍很廣:這個術語簡單意味著,編譯器具有一個隱藏的整型變數作為計數器。每當有人建立了一個指向目標對象的智能指標的一份拷貝時,編譯器就會增加這個計數器的值。當任何智能指標被刪除時,編譯器就會減少計數器的值。因此目標對象只要有需要就會存在,當所有指向它的智能指標都不存在時就會被銷毀。
以下是引用計數指標的一種實現:
template <typename T>class RefCountPtr{public:explicit RefCountPtr(T* p = NULL){Create(p);}RefCountPtr(const RefCountPtr<T>& rhs){Copy(rhs);}RefCountPtr<T>& operator=(const RefCountPtr<T>& rhs){if(ptr_ != rhs.ptr_){Kill();Copy(rhs);}return *this;}RefCountPtr<T>& operator=(T* p){if(ptr_ != p){Kill();Create(p);}return *this;}~RefCountPtr(){Kill();}T* Get()const {return ptr_;}T*operator->() const{SCPP_TEST_ASSERT(ptr_ != NULL,"Attempt to use operator -> on MULL pointer.");return ptr_;}T& operator* ()const{SCPP_TEST_ASSERT(ptr_ != NULL,"Attempt to use operator * on NULL pointer.");return *ptr_;}};
注意,這個類同時提供了拷貝建構函式和賦值操作符,因此可以複製這些指標。在這種情況下,原來的指標和複製產生的指標指向同一個對象(如果原來的指標為NULL,則複製產生的指標也為NULL)。在這層意義上,它們的行為與常規的“原始”T*指標相同。如果不再需要使用這個對象,可以通過把引用計數指標賦值為NULL將其“殺死”。
引用計數指標存在一些問題。首先,建立一個非NULL的實參具有比較大的開銷,因為編譯器使用new操作符在堆上分配一個整數,這是一種相對較為緩慢的操作。其次,引用計數指標當然並不是多安全執行緒的。雖然多線程超出了本書的範圍,但還是值得注意。如果我們確信需要複製指標,並且可以合理地確認建立一個引用計數指標所產生的開銷相對於剩餘代碼的執行速度可以忽略不計時,就可以使用引用計數指標。