標準C++類std::string的內存共享,值得體會:
詳見大牛:https://www.douban.com/group/topic/19621165/
顧名思義,內存共享,就是兩個乃至更多的對象,共同使用一塊內存;
1.關于string的內存共享問題:
通常,string類中必有一個私有成員,其是一個char*,用戶記錄從堆上分配內存的地址,其在構造時分配內存,在析構時釋放內存。
因為是從堆上分配內存,所以string類在維護這塊內存上是格外小心的,string類在返回這塊內存地址時,只返回const char*,也就是只讀的,
如果你要寫,也只能通過string提供的方法進行數據的改寫。
- #include<iostream>??
- #include<string>??
- #include<cstdio>??
- using?namespace?std;??
- ???
- main()??
- {??
- ???????string?str1?=?"hello?world";??
- ???????string?str2?=?str1;??
- ???????string?str3?=?str2;??
- ????
- ???????printf?("內存共享:\n");??
- ???????printf?("\tstr1?的地址:?%x\n",?(unsigned?int)str1.c_str()?);??
- ???????printf?("\tstr2?的地址:?%x\n",?(unsigned?int)str2.c_str()?);??
- ???????printf?("\tstr3?的地址:?%x\n",?(unsigned?int)str3.c_str()?);??
- ??
- ???????return?0;??
- }??
基本就是內存string類內存共享的最底層展現了,既然內存是一樣的了,如果需要改寫某個對象怎么辦?由此引出寫時拷貝Copy-On-Write
2.關于Copy-On-Write(原理)
顧名思義,寫的時候在拷貝,(讀的時候就不用了,哈哈)
還是以上邊的例子為例:
- #include<iostream>??
- #include<string>??
- #include<cstdio>??
- using?namespace?std;??
- ???
- main()??
- {??
- ???????string?str1?=?"hello?world";??
- ???????string?str2?=?str1;??
- ???????string?str3?=?str2;??
- ????
- ???????printf?("內存共享:\n");??
- ???????printf?("\tstr1?的地址:?%x\n",?(unsigned?int)str1.c_str()?);??
- ???????printf?("\tstr2?的地址:?%x\n",?(unsigned?int)str2.c_str()?);??
- ???????printf?("\tstr3?的地址:?%x\n",?(unsigned?int)str3.c_str()?);??
- ??
- ???????str3[1]='a';??
- ???????str2[1]='w';??
- ???????str1[1]='q';??
- ????
- ???????printf?("通過寫時拷貝之后:\n");??
- ???????printf?("\tstr1?的地址:?%x\n",?(unsigned?int)str1.c_str()?);??
- ???????printf?("\tstr2?的地址:?%x\n",?(unsigned?int)str2.c_str()?);??
- ???????printf?("\tstr3?的地址:?%x\n",?(unsigned?int)str3.c_str()?);??
- ??
- ???????return?0;??
- }??
- ??
- //輸出結果:??
- 內存共享:??
- str1?的地址:?83f9017??
- str2?的地址:?83f9017??
- str3?的地址:?83f9017??
- 通過寫時拷貝之后:??
- str1?的地址:?83f9017??
- str2?的地址:?83f9054??
- str3?的地址:?83f9034??
當開始修改是這些內存是,先不說如何實現,先表征是如何寫時拷貝的,看圖,咱還是看圖:
圖中依然說明了str3的內容修改是怎么回事,str2的內容修改,也是同樣的道理,重新給str2在堆上開辟空間,原空間只是str1一個人用,修改最后一個str1的內容時,
當然就不用在和前兩種一樣啦,因為,這個時候,原空間只有str1一個人用,這個時候,對此空間操作,沒有任何問題。都寫都可以;
寫時拷貝在此例中的體現,主要是str2,和str3內容的修改;但是有沒有發現,我每次開辟空間的同時,會在新開辟的空間開頭多分配一個空間,存放的是count;
原因就和寫時拷貝的具體操作有關了:
3.寫時拷貝(Copy-On-Write)的實現:
Copy-On-Write使用了“引用計數”,有一個變量count來計數,而且計數就放在沒開辟一段空間的開頭幾個字節。
當第一個類構造時,string的構造函數會根據傳入的參數從堆上分配內存,當有其它類需要這塊內存時,這個計數為自動累加,
當有類析構時,這個計數會減一,直到最后一個類析構時,此時的count為1或是0,此時,程序才會真正的Free這塊從堆上分配的內存。?
下面是我寫的一個簡單的例子:
- #include<iostream>??
- using?namespace?std;??
- ??
- class?String??
- {??
- public:??
- ????String(const?char*?str)??
- ????????//初始時字符創有一個\0外加4個字節的引用計數空間??
- ????????:_str(new?char[strlen(str)+5])??
- ????{??
- ????????(*((int*)_str))?=?1;//申請的空間賦值為1??
- ????????_str?+=?4;?//讓_str還是指向字符創的第一個字符??
- ???????????????????//而不是引用計數的頭上??
- ????????strcpy(_str,str);??
- ????}??
- ??
- ????String(const?String&?s)??
- ????????:_str(s._str)??
- ????{??
- ????????(*(((int*)_str)?-?1))?+=?1;??
- ????}??
- ??
- ????String&?operator=(const?String&?s)??
- ????{??
- ????????if(_str?!=?s._str)??
- ????????{??
- ????????????if(*(((int*)_str)?-?1)?==?0)??
- ????????????{??
- ????????????????delete[]?(_str-4);??
- ????????????}??
- ????????????_str?=?s._str;??
- ????????????*(((int*)_str)?-?1)?+=?1;??
- ????????}??
- ????????return?*this;??
- ??
- ??
- ????}??
- ????~String()??
- ????{??
- ????????if(*(((int*)_str)?-?1)?==?0)??
- ????????{??
- ????????????_str?-=?4;??
- ????????????delete[]?_str;??
- ????????}??
- ????}??
- private:??
- ????char?*_str;??
- };??
- ??
- void?Test()??
- {??
- ????String?s1("11111111111111111111111111");??
- ????String?s2(s1);??
- }??
- ??
- int?main()??
- {??
- ????Test();??
- ????return?0;??
- }??
到此,string類的內存共享和寫時拷貝,就算是告一段落了,個人拙見,跪求賜教!