1. 程式人生 > >C++實現智慧指標(三)

C++實現智慧指標(三)

一. 實現版本v3

  • 引用計數技術:
每一個物件負責維護物件所有引用的計數值。當一個新的引用指向物件時,引用計數器就遞增,當去掉一個引用時,引用計數就遞減。當引用計數到零時,該物件就將釋放佔有的資源。

引用計數需要儲存在被引用的資源物件裡,一個資源物件對應一個引用計數, 當其引用計數為0時,資源物件可以被銷燬。

需要修改以下函式中實現計數功能:

  1. 接收不同物件型別的建構函式:這個建構函式實現,比較簡單,直接將引用計數加1 即可
  2. 解構函式:解構函式的實現,不能再直接做delete操作,而是需要先對引用計數減1,當引用計數為0時,才做delete操作。
  3. 拷貝建構函式:拷貝建構函式的實現,底層指標共享,然後將引用計數加1 即可。
  4. 賦值操作符:賦值操作的實現,稍微複雜一些,涉及到將新指向物件的引用計數加1,將原指向物件的引用計數減1,如果有需要還要銷燬原指向物件。這裡有一點值得注意的地方,我們新的賦值操作的實現,不再需要if (this == &other) return *this;語句處理自我賦值的情況,讀者可自行分析一下我們新的賦值操作的實現為何不需要通過if語句去處理自我賦值的情況。
  • 引用計數基類
為了能夠使用引用計數技術,我們的智慧指標不能再像原生指標那樣能用可以指向任意資源物件,我們的智慧指標只能指向實現了存在方法incRefCount和方法decRefCount的資源類了。

一般按照面向物件的設計方法,我們會將管理引用計數的相關內容,抽象成一個基類,這樣任何期望能夠被智慧指標引用的資源類,只要繼承該類即可。

  • 標頭檔案 smartpointer.h
/* 
* file name : smartpointer.h
* desp : 智慧指標版本v3
*/
#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__

template <typename T>  // 將智慧指標類定義成模板類
class SmartPointer {
public:
    // 預設建構函式
    SmartPointer():mPointer(NULL) {std::cout <<"Create null smart pointer."<< std::endl;}    
    // 接收不同物件型別的建構函式
    SmartPointer(T *p):mPointer(p) {
        std::cout <<"Create smart pointer at "<<static_cast<const void*>(p)<<std::endl;
        /*智慧指標指向類T,引用計數加1*/
        if (mPointer) mPointer->incRefCount();
    }     
    // 解構函式
    ~SmartPointer(){
        std::cout << "Release smart pointer at "<<static_cast<const void*>(mPointer)<<std::endl;
        // 實現記憶體資源自動銷燬機制
        if (mPointer && mPointer->decRefCount()==0) delete mPointer;
    }
    // 拷貝建構函式
    SmartPointer(const SmartPointer &other):mPointer(other.mPointer) {
        std::cout <<"Copy smart pointer at "<<static_cast<const void*>(other.mPointer)<<std::endl;
       // 引用計數加1
       if(mPointer) mPointer->incRefCount();
    }     
   // 賦值操作符         
   SmartPointer &operator = (const SmartPointer &other) {
       T *temp(other.mPointer);
       // 新指向物件,引用計數值加1
       if (temp) temp->incRefCount();
       // 原指向物件,引用計數值減1,如果減1後引用計數為0 銷燬原資源物件
       if (mPointer && mPointer->decRefCount()==0) delete mPointer;
       // 智慧指標指向新資源物件
       mPointer = temp;  
       return *this;
   } 

private:
    T *mPointer; // 指向智慧指標實際對應的記憶體資源,根據引數自動推導規則,定義內部資源指標型別
};

/*引用計數基類*/
class RefBase   
{   
    public:   
        RefBase() : mCount(0){ }   
        void incRefCount(){   
            mCount++;   
        }   
        int decRefCount(){   
            return --mCount;
        }   
        // 除錯介面,返回物件當前引用計數   
        int getRefCount(){   
            return mCount;   
        }     
        virtual ~RefBase(){ }
    private:   
        int mCount;   
};   
#endif // __SMARTPOINTER_H__
  • 測試檔案 sptestcase3.cpp
/* 
* file name : sptestcase3.cpp
* desp : 智慧指標測試程式碼 case3 測試智慧指標的引用計數功能
*/

#include <iostream>
#include "smartpointer.h"

/*繼承於引用計數基類的SomeClass類*/
class SomeClass: public RefBase{
public:
    SomeClass(){std::cout << "SomeClass default constructor !"<<std::endl;}
    ~SomeClass(){std::cout << "SomeClass deconstructor !"<<std::endl;}
};

void testcase3(void)
{
    SomeClass *pSomeClass =  new SomeClass(); //1
    SmartPointer<SomeClass> spOuter = pSomeClass;
    std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") outer 1."<< std::endl;
    { // inner 語句塊
          SmartPointer<SomeClass> spInner = spOuter;
          std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") inner."<< std::endl;
    }
    std::cout << "SomeClass Ref Count (" << pSomeClass->getRefCount() << ") outer 2."<< std::endl;
    // delete pSomeClass ; 不需要也不能執行delete操作!

    std::cout << "new another SomeClass class for spOuter."<< std::endl;
    SmartPointer<SomeClass> spOuter2 = new SomeClass();
    spOuter = spOuter2;// 1處new出來的SomeClass將會被自動釋放  
}

int main(void)
{
    testcase3();
    return 0;
}

結果分析


  • v3版本基本實現了智慧指標功能,下一步可完善作為指標的功能:解引用、判空、比較操作

二. 知識點查漏補缺

  • 野指標
假設我們有多個智慧指標指向一塊記憶體資源,當其中某個指標被銷燬了之後,就會自動去釋放其所指向的記憶體資源,這最終會導致,其他還在使用中並且引用了改記憶體資源的智慧指標,則smartpointer2為野指標。故採用引用計數技術。