c++写时拷贝1
http://blog.csdn.net/SuLiJuan66/article/details/48882303
Copy On Write
Copy On Write(寫時復制)使用了“引用計數(shù)”(reference counting),會有一個變量用于保存引用的數(shù)量。當?shù)谝粋€類構(gòu)造時,string的構(gòu)造函數(shù)會根據(jù)傳入的參數(shù)從堆上分配內(nèi)存,當有其它類需要這塊內(nèi)存時,這個計數(shù)為自動累加,當有類析構(gòu)時,這個計數(shù)會減一,直到最后一個類析構(gòu)時,此時的引用計數(shù)為1或是0。此時,程序才會真正的Free這塊從堆上分配的內(nèi)存。
寫時復制(Copy-On-Write)技術(shù),就是編程界“懶惰行為”——拖延戰(zhàn)術(shù)的產(chǎn)物。舉個例子,比如我們有個程序要寫文件,不斷地根據(jù)網(wǎng)絡傳來的數(shù)據(jù)寫,如果每一次fwrite或是fprintf都要進行一個磁盤的I/O操 作的話,都簡直就是性能上巨大的損失,因此通常的做法是,每次寫文件操作都寫在特定大小的一塊內(nèi)存中(磁盤緩存),只有當我們關閉文件時,才寫到磁盤上?(這就是為什么如果文件不關閉,所寫的東西會丟失的原因)。
class String { public:String(char* ptr = "") //構(gòu)造函數(shù):_ptr(new char[strlen(ptr)+1]){strcpy(_ptr, ptr);}String(const String& s):_ptr(new char[strlen(s._ptr)+1])//另外開辟空間{strcpy(_ptr, s._ptr);}~String(){if (_ptr){delete[] _ptr;}} private:char* _ptr; };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
-
GetTickCount : 在Release版本中,該函數(shù)從0開始計時,返回自設備啟動后的毫秒數(shù)(不含系統(tǒng)暫停時間)。在頭文件windows.h中。
-
在上面for循環(huán)中,語句“String s2 = s1;”不斷調(diào)用拷貝構(gòu)造函數(shù)為s2開辟空間,執(zhí)行完語句“String s2 = s1;”后,不斷調(diào)用析構(gòu)函數(shù)對s2進行釋放,導致低效率,Test執(zhí)行結(jié)果如下圖:
- 寫時拷貝~~寫時拷貝~自然是我們自己想寫的時候再進行拷貝(復制),下面引入幾種方案如下:(試著判斷哪一種方案可行)
- 這里又引入另外一個概念“引用計數(shù)”:string的構(gòu)造函數(shù)會根據(jù)傳入的參數(shù)從堆上分配內(nèi)存,當有其它類需要這塊內(nèi)存時(即其它對象也指向這塊內(nèi)存),這個計數(shù)為自動累加,上面方案中的_retCount就是用來計數(shù)的。
- 簡單地介紹一下上面三個方案。方案一和方案二是不可行的,方案一中的_retCount是屬于每個對象內(nèi)部的成員,當有多個對象同時指向同一塊空間時,_retCount無法記錄多個對象;方案二中的_retCount是靜態(tài)成員變量,是所有對象所共有,似乎可以記錄,舉個例子:對象s1、s2指向A空間,_retCount為2,對象s3、s4指向B空間,此時_retCount變?yōu)?,但是當想釋放B空間時,應當在析構(gòu)函數(shù)中_retCount減到0時釋放,但是當_retCount減到0時,卻發(fā)現(xiàn)釋放的是A空間,而B空間發(fā)生了內(nèi)存泄露。也就是靜態(tài)成員變量_retCount只能記錄一塊空間的對象個數(shù)。
-?下面通過代碼介紹方案三:
class String { public:String(char* ptr = "") //構(gòu)造函數(shù):_ptr(new char[strlen(ptr)+1]), _retCount(new int(1))//每個對象對應一個整型空間存放{ //指向這塊空間的對象個數(shù)strcpy(_ptr, ptr);}String(const String& s) //拷貝構(gòu)造函數(shù):_ptr(s._ptr), _retCount(s._retCount){_retCount[0]++;}String& operator= (const String& s) //賦值運算符重載{if (this != &s){if (--_retCount[0] == 0){//舊的引用計數(shù)減1,如果是最后一個引用對象,則釋放對象delete[] _ptr;delete[] _retCount;}_ptr = s._ptr;//改變this的指向,并增加引用計數(shù)_retCount = s._retCount;++_retCount[0];}return *this;}~String(){if (--_retCount[0] == 0){delete[] _ptr;delete[] _retCount;}} private:char* _ptr;int* _retCount; };- 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
- 同樣執(zhí)行Test函數(shù),測試結(jié)果如下圖:
下面進一步優(yōu)化方案三來介紹寫時拷貝(寫時復制)
方案三:是每個對象對應一個整型空間(即_refCount)存放指向這塊空間的對象個數(shù)
再優(yōu)化:不引用_refCount,但每次給_ptr開辟空間的時候,多開辟四個字節(jié),用來記錄指向此空間的對象個數(shù),規(guī)定用開頭那四個字節(jié)來計數(shù)。
class String { public:String(char* ptr = ""):_ptr(new char[strlen(ptr)+5]){_ptr += 4;strcpy(_ptr,ptr);_GetRefCount(_ptr) = 1;//每構(gòu)造一個對象,頭四個字節(jié)存放計數(shù)}String(const String& s):_ptr(s._ptr){_GetRefCount(_ptr)++; //每增加一個對象,引用計數(shù)加1}String& operator= (const String& s){if (this != &s){Release(_ptr);_ptr = s._ptr;_GetRefCount(_ptr)++;}return *this;}char& operator [](size_t index){if (_GetRefCount(_ptr) > 1){--_GetRefCount(_ptr);//舊引用計數(shù)減1char* str = new char[strlen(_ptr) + 1];//另外開辟一個空間str += 4;strcpy(str, _ptr);_GetRefCount(str) = 1;_ptr = str;}}~String(){Release(_ptr);}inline void Release(char* ptr){if (--_GetRefCount(ptr) == 0){delete[](ptr - 4);}}inline int& _GetRefCount(char* ptr){return *(int*)(ptr - 4);//訪問頭四個字節(jié)} private:char* _ptr; };- 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
程序執(zhí)行過程,看下圖說話
對下列函數(shù)進行解析:
char& operator [](size_t index){if (_GetRefCount(_ptr) > 1){--_GetRefCount(_ptr);//舊引用計數(shù)減1char* str = new char[strlen(_ptr) + 1];//另外開辟一個空間str += 4;strcpy(str, _ptr);_GetRefCount(str) = 1;_ptr = str;}}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
當在主函數(shù)中執(zhí)行語句:s1[0] = ‘w’;時,想要改變s1對象中_ptr[0]的值;但是當我們改變s1中_ptr[0]的值時,不希望把s2、s3中_ptr[0]的值也改變了。由于s1、s2、s3目前指向同一塊空間,改變其中一個,另外兩個肯定也跟著改變了,所以提供了另外一種方法:把對象s1分離出來,舊引用計數(shù)減1,另外給s1開辟一段跟原來一樣的空間,存放一樣的內(nèi)容,這時候即使改變了s1的內(nèi)容,也不影響s2、s3的對容。
一樣看下圖說話:
總結(jié)
- 上一篇: C++总结8——shared_ptr和w
- 下一篇: 请问,游戏今天是不是又更新了?