智能指針與句柄類(三)

  以前文章中實現的寫時複製,句柄類中引用計數和T類型指針是分開的,這裏換一種方式來處理,將引用計數和T類型指針視爲一個總體,當作句柄類模板參數。先對上節中的引用計數進行改造:html

這個版本的UseCount和以前的版本差異很大,從析構函數能夠看出(純虛函數),它是基於引用計數來共享的值對象的基類,須要注意的部分:ide

 1 class CUseCount 
 2 { 
 3 public: 
 4     CUseCount(); 
 5     CUseCount(const CUseCount&);
 6     CUseCount& operator=(const CUseCount&);
 7     virtual ~CUseCount() = 0; 
 8  
 9     void markUnshareable();
10     bool isShareable() const;
11     bool isShared() const;
12     
13     void addReference();
14     void removeReference();
15  
16 private:
17     int refCount;    //注意這裏非int指針
18     bool shareable;    //是不是共享狀態
19 };
20 
21 CUseCount::CUseCount():refCount(0), shareable(true)
22 {} 
23  
24 CUseCount::CUseCount(const CUseCount& u):refCount(0), shareable(true)
25 {}
26 
27 CUseCount& CUseCount::operator=(const CUseCount& u)
28 {
29     return *this;
30 }
31 
32 CUseCount::~CUseCount() 
33 {} 
34 
35 void CUseCount::markUnshareable()         
36 {          
37     shareable = false;          
38 }
39         
40 bool CUseCount::isShareable() const
41 {
42     return shareable;         
43 }
44    
45 bool CUseCount::isShared() const
46 {         
47     return refCount > 1;         
48 }
49 
50 void CUseCount::addReference() 
51 {
52     ++refCount;
53 }
54 
55 void CUseCount::removeReference()         
56 {          
57     if(--refCount == 0)
58         delete this;         
59 }
View Code
  • 構造函數中refCount設置爲0而不是1,說明此時尚未某個對象在引用它。對refCount的增長由構造它的對象調用addReference來實現。shareable用了表示此對象是否能夠被共享。
  • 拷貝構造函數並無根據拷貝的對象進行一些初始化動做,而是與構造函數完成相同的功能。想一想在什麼狀況下會調用UseCount的拷貝構造函數,是在句柄類不在共享狀態時,進行寫時複製,須要調用拷貝構造函數,而這裏UseCount是不共享的,因此一樣設置refCount=0,shareable=true。
  • 賦值操做符沒有作任何事情,返回自身,沒有將其設爲private狀態是由於UseCount的派生類可能調用operator=,從而調用到基類的operator=。
  • UseCount析構函數沒有作任何事情,此版本中UseCount和T類型指視爲一個總體一塊兒當作句柄類的模板參數而且建立在堆上,這裏句柄類調用引用計數的增長(addReference)與減小(removeReference),並不直接調用delete,刪除引用計數,UseCount的銷燬是經過removeReference中refCount的值來判斷的,因爲使用了delete this,因此必須保證此對象建立在堆上。

  接下來看看這一版本中的Handle類:函數

 1 template<class T> class Handle  2 {  3 public:  4     Handle(T *p = 0);  5     Handle(const Handle& h);  6     Handle& operator=(const Handle&);  7     ~Handle();  8     //other member functions
 9 private: 10     T* ptr; 11     void init(); 12 }; 13 
14 template<class T>
15 inline Handle<T>::init() 16 { 17     if(ptr == 0) 18         return; 19     if(ptr->isShareable() == false)    //非共享狀態,進行復制
20         ptr = new T(*ptr); 21 
22     ptr->addReference();    //引用計數增長
23 } 24 
25 template<class T>   
26 inline Handle<T>::Handle(T* p):ptr(p) //u 默認構造函數
27 { 28  init(); 29 } 30 
31 template<class T>
32 inline Handle<T>::Handle(const Handle& rhs):ptr(rhs.ptr) 33 { 34  init(); 35 } 36 
37 template<class T>
38 inline Handle<T>& Handle<T>::operator=(const Handle& rhs) 39 { 40     if(ptr != rhs.ptr) 41  { 42         if(ptr != NULL) 43  { 44             ptr->removeReference(); 45  } 46         ptr = rhs.ptr; 47  init(); 48  } 49     return *this; 50 } 51 
52 template<class T>
53 inline Handle<T>::~Handle() 54 { 55     if(ptr) 56         ptr->removeReference(); 57 }
View Code

修改後的Handle類比以前的要複雜一些:this

  • 觀察ptr調用的函數,不難看出T類型繼承自UseCount。
  • init()函數提供統一接口,對T類型進行寫時複製,並完成對引用技術值的增長。
  • 析構函數並不delete ptr,而是減小引用計數值,讓其本身判斷是否析構。

  使用本節中實現的UseCount和Handle來處理寫時複製,一個String類的例子以下:spa

 1class String  2 {  3 public:  4     String(const char *value = "");  5     const char& operator[](int index) const;  6     char& operator[](int index);  7 private:  8     struct StringValue: public CUseCount    //繼承自引用計數
 9  { 10         char *data; 11         StringValue(const char *initValue); 12         StringValue(const StringValue& rhs); 13         void init(const char *initValue); 14         ~StringValue(); 15  }; 16     Handle<StringValue> value; 17 }; 18 
19 void String::StringValue::init(const char *initValue) 20 { 21     data = new char[strlen(initValue) + 1]; 22  strcpy(data, initValue); 23 } 24 
25 String::StringValue::StringValue(const char *initValue) 26 { 27  init(initValue); 28 } 29 
30 String::StringValue::StringValue(const StringValue& rhs) 31 { 32  init(rhs.data); 33 } 34 
35 String::StringValue::~StringValue() 36 { 37  delete [] data; 38 } 39 
40 String::String(const char *initValue): value(new StringValue(initValue)) 41 {} 42 
43 const char& String::operator[](int index) const    //const函數不進行復制
44 { 45     return value->data[index]; 46 } 47 
48 char& String::operator[](int index) 49 { 50     if (value->isShared()) 51  { 52         value = new StringValue(value->data);    //先調用基類UseCount拷貝構造函數
53  } 54     value->markUnshareable(); 55     return value->data[index]; 56 }
View Code

代碼中能夠看出,String內部實現由StringValue完成,Handle句柄託管StringValue類型指針,StringValue繼承自UseCount類,其子對象部分負責char*的管理,,父對象即UseCount完成對象的計數、共享以及銷燬工做,以圖來表示例子中各個類的關係以下:3d

代碼中還有一些須要的地方:指針

  • StringValue是一箇中間層,實現char*的管理和引用計數功能,而對用戶來講它是不可見的,Handle對象實現對StringValue的託管。
  • String類在實現進行寫時複製的函數時,須要操做引用計數對象,因爲Handle對StringValue進行了託管,全部操做都在Handle對象上進行。
  • const版本的operator[]沒有用到寫時複製,在非const版本中進行寫時複製。
  • 進行復制的條件if(value->isShared()) ,而不是if(value->isShareable()),注意這二者的區別,isShared()表示當前句柄託管的對象是否與其餘句柄共享,而isShareable()表示是否能夠進行共享。舉個例子,若是將判斷條件改成if(value->isShareable()),以下代碼:
     1 char& String::operator[](int index)  2 {  3     if (value->isShareable())  4  {  5         value = new StringValue(value->data);    //先調用基類UseCount拷貝構造函數
     6  }  7     value->markUnshareable();  8     return value->data[index];  9 } 10 
    11 int main() 12 { 13     String s("Grubby"); 14     char c = s[3]; 15     
    16     return 0; 17 }
    View Code

    main函數中line14,調用了operator[] 操做符,按上述代碼實現,line3先判斷if(value->isShareable()),而s對象剛剛定義沒有使用,UseCount構造函數中設置isShare=true,因此if判斷返回true,運行語句value = new StringValue(value->data),從新構造value是沒有必要的。而判斷若是是if(isShared()),此時refCount==1,因此沒有共享if返回false,不會從新構造value。code

  這一節中的代碼,借鑑了《more effective C++》中的引用計數章節,加上了一些本身的我的理解,文章中難免有誤,還請你們指正。htm

  未完待續……對象

相關文章
相關標籤/搜索