【轉載】C++應用引用計數技術

來源:互聯網
上載者:User

標籤:

原帖:http://www.cnblogs.com/chain2012/archive/2010/11/12/1875578.html

因為Windows的核心對象也運用了引用計數,所以稍作瞭解並非無用。

引用計數可以讓多個對象共用一個資料,而且免除了跟蹤控制權的負擔,讓對象自己管理自己,當再沒有被使用時可以自動刪除,也算是一種簡易的記憶體回收機制。

另一方面,如果有N多個相同的對象:○=○=○=○=...=○=○ 這樣的做法是臃腫且無聊的,所以一個好的做法就是讓對象可以共用這一個資料。既可以節省記憶體,又可以提高效率讓程式負擔更少,不用構造和析構這個值對象的拷貝了。

1 String a, b, c, d, e;
2 a=b=c=d=e="hello";
1 String& String::operator=(const String &rhs)
2 {
3 if (data==&rhs) return *this; //防止自我賦值
4   delete [] data;
5 data = new char[strlen(rhs.data)+1)];
6 strcpy(data, rhs.data);
7 return *this;
8 }

用圖顯示的話,即:

當a被賦予了另外的值,a="world"; 這時候不能刪除這個Hello,應外仍然存在bcde,4個對象在共用這個資料;另外,當只有1個對象x在用這個Hello,而x已經超過了其生存期,沒有其他對象指向這個Hello的時候,我們需要刪除這個Hello確保不發生資源泄漏。這也就意味著引入引用計數後,圖將改變成這樣:

 

  • 實現引用計數

應該是每一個String值對應一個計數數值,而不是String對象對應一個引用計數。接下來,建立一個嵌套類StringValue來儲存計數和其跟蹤的值。

String.h
 1 #include <string>
2
3  class String {
4  public:
5 String(const char *initValue="");
6 String& String::operator=(const String &rhs);
7
8  private:
9 // StringValue的主要目的是提供一個空間將一個特別的值和共
10 // 享此值的對象的數目聯絡起來
11 struct StringValue //嵌套類,引用計數
12 {
13 int refCount; //計數數值
14 char *data;
15 StringValue (const char* initValue);
16 ~StringValue();
17 };
18 StringValue *value;
19 };
String.cpp
 1 #include "String.h"
2
3 String::StringValue::StringValue(const char* initValue)
4 :refCount(1)
5 {
6 data = new char[strlen(initValue)+1];
7 strcpy(data, initValue);
8 }
9
10 String::StringValue::~StringValue()
11 {
12 delete [] data;
13 }
14
15 String::String(const char *initValue)
16 :value(new StringValue(initValue))
17 {
18
19 }

而這樣做通常會產生一個問題,

String s1("More Effective C++");

String s2("More Effective C++");

將會變成這樣的資料結構:

想辦法改進一下:

控制副本的簡單實現
 1 list<string> String::StringValue::independObj; //獨立對象
2  String::StringValue::StringValue(const char* initValue)
3 :refCount(1)
4 {
5 typedef list<string>::iterator lsp;
6 lsp p = find(independObj.begin(), independObj.end(), string(initValue));
7 if (p==independObj.end()||independObj.empty())
8 {//未找到對象,建立
9   data = new char[strlen(initValue)+1];
10 strcpy(data, initValue);
11 independObj.push_back(string(data));
12 }
13 else
14 {
15 // do something...
16   }
17 }

接下來看下String類的拷貝建構函式

String::String(const String& rhs) 
: value(rhs.value)
{
++value->refCount;
}

當這樣構造2個對象:

String s1("More Effective C++");

String s2(s1);

就會產生這樣的資料結構,其代價是非常低廉的,省去了新對象的構造(不必分配新記憶體和把內容拷貝到這塊記憶體中)和之後的析構(不必釋放那塊記憶體),僅僅是使計數+1和拷貝了下指標

拷貝建構函式之後看下解構函式

String::~String()
{
if (--value->refCount == 0)
{
delete value;
}
}

即,當被引用的對象還有其他共用對象時,僅把計數-1;而當沒有其他共用對象時,才徹底將引用對象析構掉。接著,是重載賦值操作符,稍微有些複雜

String& String::operator=(const String &rhs)
{
if (value == rhs.value) //賦值的是其本身
return *this; //什麼也不做
if (--value->refCount == 0) //如果只有當前對象在共用那個資料
delete value; //則刪除掉,因為即將被賦予新的引用。不是的話,僅將計數-1
value = rhs.value; //賦值操作
++value->refCount; //計數器+1
return *this;
}
  • 寫時拷貝

const版本的下標操作僅僅是唯讀,不會對引用對象做出修改

const char& String::operator[](int index) const  //const版本
{
//需下標溢出檢查
return value->data[index];
}

需要考慮的是非const版本的下標操作,因為C++編譯器無法告訴我們非const的operator[]是會被用來讀還是寫操作。所以我們保守地認為所有的操作都是“寫”的。

char& String::operator[](int index)  //非const版本
{
if (value.refCount>1) //如果引用對象不止一個
{
--value.refCount; //計數減一,相當於把這個引用刪除了
value = new StringValue(value->data); //重新申請一份新的拷貝
}
return value->data[index];
}

這個思想就是:“與其他對象共用的一個值直到寫操作時才擁有自己的拷貝”。即,lazy原則的特例。

  • 指標、引用與寫時拷貝

在大部分情況下都能滿足以上的應用,可是唯一情況卻頗為棘手,比如

String s1("More Effective C++");

char* p=&s1;

String s2 = s1;

拷貝建構函式讓s2和s1共用這個對象,這時候的資料結構為

如果寫下這樣一句: p[1]=‘X‘; //將同時修改s1和s2的內容!String 的拷貝建構函式無法檢測出s1擁有指向StringValue指標的存在。該問題的一個解決方案就是:在每個StringValue中增加一個標誌,表示該對象是否可以被共用。在最初是ture狀態,而在調用了非const的operator[]之後則設定成false,且之後永遠置於false狀態。

追加共用標誌位的String
  • 帶引用計數的基類

引用計數不僅運用在字串類上,只要是多個對象共用相同值的類都可以。

構建一個基類(RCObject),任何需要引用計數的類都必須繼承自此類。由RCObject類封裝引用計數功能。

 

RCObject.hRCObject.cpp
  • 自動引用計數處理

 

RCObject類給了我們一個儲存引用計數的地方,並提供了成員函數供我們操作引用計數,但調用這些函數的動作還必須被手工加入其它類中。仍然需要在String的拷貝建構函式和賦值運算函數中調用StringValue的addReference和 removeReference函數。這很笨拙。

StringValue *value; 必須操作StringValue對象的refCount欄位。是否能夠讓指標自身檢測發生複製拷貝,賦值操作,析構操作此類事件,而對於計數經行修改的操作呢?答案是否定的。代替的方法就是利用智能指標。

【分析】

開始看函數式treap的時候看到的...

話說函數式treap消耗的記憶體那麼大嗎?還要用引用計數。

表示只用記憶體池。

 順便附上一份網上函數式treap模板...

http://ideone.com/kbSjPp

【轉載】C++應用引用計數技術

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.