還記得書本上的關于深拷貝和淺拷貝的問題嗎?
如果自己寫的一個類TestClass,采用的是預設的構造函數,或者采用自定義的構造函數(但是沒有實作深拷貝)。那麼下面的代碼:
class TestClass
{
public:
char * p;
};
TestClass a;
TestClass b(a);
TestClass c = a;
上訴都是淺拷貝,也就是a.p == b.p == c.p;如果要想深拷貝,必須由你自己用代碼實作。
STL中string的情況呢?
首先來看看下面語句的語義(也就是你這樣寫實際上是想要達到什麼目的,或者是大家一看就知道的想要達到的目的):
string str1;
string str2(str1);
string str3 = str1;
上訴的三句實際上是想實作的什麼呢?答案是深拷貝。實際上隻要是采取上訴的指派操作符或者是拷貝構造函數,使用者都是想實作的是深拷貝。那麼作為STL string的開發實作者就必須要實作string拷貝構造函數和=操作符的深拷貝。
但是這裡string做了優化,做到對記憶體管理的優化。
這裡不同版本的string采用了不同的方法:COW和SSO
看如下代碼:
string str1("123456");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str()); //1 step
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str()); //2 step
如果string采取的是COW的實作方式,那麼1 step輸出的str1.c_str()和str2.c_str()的值相等,說明str1和str2共享了字元串數組這部分的記憶體空間。(因為我的VS2010采用的是SSO方式,是以這裡不能貼出實際的實驗結果)。
但是當str1和str2中有任何一方的資料發生更改的時候,就會發生重新申請記憶體的操作和拷貝操作(因為兩個string資料不一樣了,不能再共享了)。
Copy On Write(COW)相當于是把申請記憶體和拷貝操作往後拖延了,直到不得不這樣做時才這樣做。這種方法是把導緻配置設定支援的耗時操作往後延遲的方法。可能導緻多個延遲操作在後面的某一時間同時需要統一完成(多個string資料同時發生改變),那麼會導緻後面的某個時間點會出現比較大的累計延遲。可以說有利也有弊。
再看看如下的代碼:
string str1("123456789654464444444461322222222222222222222222222222222222222363333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333331111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
運作結果如下:
這是字元串比較長的時候的string申請記憶體空間的情況。很顯然字元串數組(str1.c_str())的位址和string對象的位址差别較大,前者處于堆空間,後者處于函數的棧空間。這種情況一般就是我們所認識的深拷貝了,我們自己寫的類的深拷貝也就是這種情況。
再看下面的代碼:
string str1("123456");
string str2(str1);
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
str1[0] = '1';
printf("%d***************%d +++++++ %d***************%d\n", &str1, str1.c_str(), &str2, str2.c_str());
運作結果是:
從結果可以看出&str1和str1.c_str()的值比較接近,他們都屬于函數的棧空間。這就說明此時的string内部的字元串數組沒有去堆上申請空間,而是足夠小的字元串直接存在了對象本身的棧記憶體中。是以這種情況str1和str2是沒有共享記憶體的,所有的資料都在各自的對象中。這在語義上是深拷貝,但是和我們自己實作的深拷貝不一樣。這就是有的STL string版本實作的(small string optimization)SSO方式。VS2010就是采用的SSO方式。