C++ String类写时拷贝 4
http://blog.51cto.com/zgw285763054/1839752
?維基百科:
??? 寫入時復制(英語:Copy-on-write,簡稱COW)是一種計算機程序設計領域的優化策略。其核心思想是,如果有多個調用者(callers)同時要求相同資源(如內存或磁盤上的數據存儲),他們會共同獲取相同的指針指向相同的資源,直到某個調用者試圖修改資源的內容時,系統才會真正復制一份專用副本(private copy)給該調用者,而其他調用者所見到的最初的資源仍然保持不變。這過程對其他的調用者都是透明的(transparently)。此作法主要的優點是如果調用者沒有修改該資源,就不會有副本(private copy)被創建,因此多個調用者只是讀取操作時可以共享同一份資源。
? ? String類中的寫時拷貝技術是指用淺拷貝的方法拷貝其他對象,多個指針指向同一塊空間,只有當對其中一個對象修改時,才會開辟一個新的空間給這個對象,和它原來指向同一空間的對象不會受到影響。寫時拷貝的效率遠高于深拷貝。
? ? 可以通過增加一個成員變量count來實現寫時拷貝,這個變量叫做引用計數,統計這塊空間被多少個對象的_str同時指向。當用指向這塊空間的對象拷貝一個新的對象出來時count+1,當指向這塊空間的一個對象指向別的空間或析構時count-1。只有當count等于0時才可以釋放這塊空間,否則說明還有其他對象指向這塊空間,不能釋放。
? ? count應該是什么類型呢?如果是int類型。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class?String { ????public: ????String(const?char*?str) ????????:_str(new?char[strlen(str)+1]) ????????,_count(1) ????{ ????????strcpy(_str,?str); ????} ????String(String&?s) ????????:_str(s._str) ????{ ????????++s._count; ????????_count?=?s._count; ????} ????~String() ????{ ????????if?(--_count?==?0) ????????{ ????????????delete[]?_str; ????????} ????} private: ????char*?_str; ????int?_count; }; void?Test() { ????String?s1("aaaaaaaaa"); ????String?s2(s1); } |
雖然s1._count和s2._count都等于2,但是當s2執行析構函數后
? ? 現在只剩下s1一個對象指向這塊空間,s1._count和s2._count應該都變為1,但是s1._count沒有改變,查看s1._count和s2._count的地址發現它們并不是同一個地址,改變count只對當前對象有效,其他對象不會受到影響,無法實現引用計數。
這說明count是公共的,可以被多個對象同時訪問的。如果是static int類型
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | class?String { ????public: ????String(const?char*?str) ????????:_str(new?char[strlen(str)+1]) ????{ ????????_count?=?1; ????????strcpy(_str,?str); ????} ????String(String&?s) ????????:_str(s._str) ????{ ????????++_count; ????} ????~String() ????{ ????????if?(--_count?==?0) ????????{ ????????????delete[]?_str; ????????} ????} private: ????char*?_str; ????static?int?_count; }; int?String::_count?=?0; void?Test() { ????String?s1("aaaaaaaaa"); ????String?s2(s1); ????String?s3(s2); ????String?s4("bbbbbbbbb"); ????String?s5(s4); } |
? ? 現在s1、s2、s3的引用計數應該是3,s4、s5的引用計數應該是2。
? ? 但是結果不正確。原因是s1、s2、s3指向同一塊空間后count增加到3,構造s4時又把count設置為1,s4拷貝構造s5后count增加到2。說明這5個對象共用一個count,不能實現引用計數。
? ? 如果一個對象第一次開辟空間存放字符串時再開辟一塊新的空間存放引用計數,當它拷貝構造其他對象時讓其他對象的引用計數都指向存放引用計數的同一塊空間,count設置為int*類型,就可以實現引用計數了。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | class?String ????{ ????public: ????????String(const?char*?str) ????????????:_str(new?char[strlen(str)+1]) ????????????,_pCount(new?int(1)) ????????{ ????????????strcpy(_str,?str); ????????} ????????String(String&?s) ????????????:_str(s._str) ????????????,_pCount(s._pCount) ????????{ ????????????++(*_pCount); ????????} ????????String&?operator=(const?String&?s) ????????{ ????????????if?(/*this?!=?&s?||*/?_str?!=?s._str)?//防止自己給自己賦值,或自己拷貝的對象給自己賦值 ????????????{ ????????????????//釋放原對象 ????????????????if?(--(*_pCount)?==?1) ????????????????{ ????????????????????delete?_pCount; ????????????????????delete[]?_str; ????????????????} ????????????????//淺拷貝增加引用計數 ????????????????_str?=?s._str; ????????????????_pCount?=?s._pCount; ????????????????++(*_pCount); ????????????} ????????????return?*this; ????????} ????????~String() ????????{ ????????????if?(--*_pCount?==?0) ????????????{ ????????????????delete?_pCount; ????????????????delete[]?_str; ????????????} ????????} ????protected: ????????char*?_str; ????????int*?_pCount; ????}; |
? ?但是這種方法也存在不足:
? ? 1、它每次new兩塊空間,創建多個對象時效率較低于下面這種方法;
? ? 2、它多次分配小塊空間,容易造成內存碎片化,導致分配不出來大塊內存。
? ? 還有一種方法是在開辟_str時多開辟4個字節,在這塊空間的頭部保存引用計數。
? ??
? ??
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | class?String { public: ????String(const?char*?str) ????????:_str(new?char[strlen(str)+5]) ????{ ????????_str?+=?4; ????????strcpy(_str,?str); ????????//(*(int*)(_str-4))?=?1; ????????_GetRefCount(_str)?=?1; ????} ????String(const?String&?s) ????????:_str(s._str) ????{ ????????//*((int*)(_str-4))?+=?1; ????????_GetRefCount(_str)++; ????} ????String&?operator=(const?String&?s) ????{ ????????if?(/*this?!=?&s?||*/?_str?!=?s._str)?//防止自己給自己賦值,或自己拷貝的對象給自己賦值 ????????{ ????????????/*if?(--(*(int*)(_str-4))?==?0) ????????????{ ????????????????delete[]?(_str-4); ????????????}*/ ????????????_Release(); ????????????_str?=?s._str; ????????????++(*(int*)(s._str-4)); ????????} ????????return?*this; ????} ????~String() ????{ ????????/*if?(--(*(int*)(_str-4))?==?0) ????????{ ????????????delete[]?(_str-4); ????????}*/ ????????_Release(); ????} ????//operator[]的特殊性,讀時也拷貝 ????char&?operator[](size_t?index) ????{ ????????//當引用計數大于1,需要寫時拷貝 ????????if?(_GetRefCount(_str)?>?1) ????????{ ????????????char*?tmp?=?new?char[strlen(_str)?+?5]; ????????????--_GetRefCount(_str);?//new空間后再減引用計數,防止new空間失敗 ????????????tmp?+=?4; ????????????_GetRefCount(tmp)?=?1; ????????????_str?=?tmp; ????????} ????????return?_str[index]; ????} protected: ????int&?_GetRefCount(char*?_ptr) ????{ ????????return?*((int*)(_ptr-4)); ????} ????//--引用計數,如果引用計數等于0,釋放 ????void?_Release() ????{ ????????if?(/*--(*(int*)(_str-4))*/--_GetRefCount(_str)?==?0) ????????{ ????????????delete[]?(_str-4); ????????} ????} protected: ????char*?_str; }; |
? ??
| 1 2 3 4 5 6 7 8 9 10 11 12 | void?COWTest() ????{ ????????String?s1("aaaaaaaaaaa"); ????????String?s2(s1); ????????String?s3(s1); ????????//operator[]的特殊性,讀時也拷貝 ????????cout<<s1[0]<<endl; ????????? ????????//寫時拷貝 ????????s1[0]?=?'1'; ????} |
? ? 當對s1修改后,s1指向新拷貝出來的空間。
? ??
? ? 推薦文章:??《C++ STL string的Copy-On-Write技術》
總結
以上是生活随笔為你收集整理的C++ String类写时拷贝 4的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 英雄联盟手游阿卡丽天赋怎么点
- 下一篇: C++智能指针(一)智能指针的简单介绍