標準C++類std::string的Copy-On-Write技術

標準C++類std::string的

內存共享和Copy-On-Write技術

 

陳皓

1、             概念

 

Scott Meyers在《More Effective C++》中舉了個例子,不知你是否還記得?在你還在上學的時候,你的父母要你不要看電視,而去複習功課,於是你把自己關在房間裏,做出一副正在複習功課的樣子,其實你在幹着別的諸如給班上的某位女生寫情書之類的事,而一旦你的父母出來在你房間要檢查你是否在複習時,你才真正撿起課本看書。這就是“拖延戰術”,直到你非要做的時候纔去做。

 

當然,這種事情在現實生活中時往往會出事,但其在編程世界中搖身一變,就成爲了最有用的技術,正如C++中的可以隨處聲明變量的特點一樣,Scott Meyers推薦我們,在真正需要一個存儲空間時纔去聲明變量(分配內存),這樣會得到程序在運行時最小的內存花銷。執行到那纔會去做分配內存這種比較耗時的工作,這會給我們的程序在運行時有比較好的性能。必竟,20%的程序運行了80%的時間。

 

當然,拖延戰術還並不只是這樣一種類型,這種技術被我們廣泛地應用着,特別是在操作系統當中,當一個程序運行結束時,操作系統並不會急着把其清除出內存,原因是有可能程序還會馬上再運行一次(從磁盤把程序裝入到內存是個很慢的過程),而只有當內存不夠用了,纔會把這些還駐留內存的程序清出。

 

寫時才拷貝(Copy-On-Write)技術,就是編程界“懶惰行爲”——拖延戰術的產物。舉個例子,比如我們有個程序要寫文件,不斷地根據網絡傳來的數據寫,如果每一次fwrite或是fprintf都要進行一個磁盤的I/O操作的話,都簡直就是性能上巨大的損失,因此通常的做法是,每次寫文件操作都寫在特定大小的一塊內存中(磁盤緩存),只有當我們關閉文件時,才寫到磁盤上(這就是爲什麼如果文件不關閉,所寫的東西會丟失的原因)。更有甚者是文件關閉時都不寫磁盤,而一直等到關機或是內存不夠時才寫磁盤,Unix就是這樣一個系統,如果非正常退出,那麼數據就會丟失,文件就會損壞。

 

呵呵,爲了性能我們需要冒這樣大的風險,還好我們的程序是不會忙得忘了還有一塊數據需要寫到磁盤上的,所以這種做法,還是很有必要的。

 

 

2、             標準C++std::stringCopy-On-Write

 

在我們經常使用的STL標準模板庫中的string類,也是一個具有寫時才拷貝技術的類。C++曾在性能問題上被廣泛地質疑和指責過,爲了提高性能,STL中的許多類都採用了Copy-On-Write技術。這種偷懶的行爲的確使使用STL的程序有着比較高要性能。

 

這裏,我想從C++類或是設計模式的角度爲各位揭開Copy-On-Write技術在string中實現的面紗,以供各位在用C++進行類庫設計時做一點參考。

 

在講述這項技術之前,我想簡單地說明一下string類內存分配的概念。通過常,string類中必有一個私有成員,其是一個char*,用戶記錄從堆上分配內存的地址,其在構造時分配內存,在析構時釋放內存。因爲是從堆上分配內存,所以string類在維護這塊內存上是格外小心的,string類在返回這塊內存地址時,只返回const char*,也就是隻讀的,如果你要寫,你只能通過string提供的方法進行數據的改寫。

 

2.1、         特性

 

由表及裏,由感性到理性,我們先來看一看string類的Copy-On-Write的表面特徵。讓我們寫下下面的一段程序:

 


#include

#include

using namespace std;

 

main()

{

       string str1 = "hello world";

       string str2 = str1;

      

       printf ("Sharing the memory:/n");

       printf ("/tstr1's address: %x/n", str1.c_str() );

       printf ("/tstr2's address: %x/n", str2.c_str() );

      

    str1[1]='q';

       str2[1]='w';

 

       printf ("After Copy-On-Write:/n");

       printf ("/tstr1's address: %x/n", str1.c_str() );

       printf ("/tstr2's address: %x/n", str2.c_str() );

 

       return 0;

}

 

這個程序的意圖就是讓第二個string通過第一個string構造,然後打印出其存放數據的內存地址,然後分別修改str1str2的內容,再查一下其存放內存的地址。程序的輸出是這樣的(我在VC6.0g++ 2.95都得到了同樣的結果):

 


> g++ -o stringTest stringTest.cpp

> ./stringTest

Sharing the memory:

        str1's address: 343be9

        str2's address: 343be9

After Copy-On-Write:

        str1's address: 3407a9

        str2's address: 343be9

 

從結果中我們可以看到,在開始的兩個語句後,str1str2存放數據的地址是一樣的,而在修改內容後,str1的地址發生了變化,而str2的地址還是原來的。從這個例子,我們可以看到string類的Copy-On-Write技術。

 

發佈了23 篇原創文章 · 獲贊 2 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章