日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

c++写时拷贝1

發(fā)布時間:2023/11/30 c/c++ 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++写时拷贝1 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

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)絡(luò)傳來的數(shù)據(jù)寫,如果每一次fwrite或是fprintf都要進行一個磁盤的I/O操 作的話,都簡直就是性能上巨大的損失,因此通常的做法是,每次寫文件操作都寫在特定大小的一塊內(nèi)存中(磁盤緩存),只有當我們關(guān)閉文件時,才寫到磁盤上?(這就是為什么如果文件不關(guān)閉,所寫的東西會丟失的原因)。

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
void Test() {String s1 = "hello world";int begin = GetTickCount();//記錄此時毫秒數(shù)for (int i = 0; i < 10000; ++i){String s2 = s1;}int end = GetTickCount();//記錄此時毫秒數(shù)cout << "cost time:" << end - begin << endl; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  • GetTickCount : 在Release版本中,該函數(shù)從0開始計時,返回自設(shè)備啟動后的毫秒數(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空間時,應(yīng)當在析構(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))//每個對象對應(yīng)一個整型空間存放{ //指向這塊空間的對象個數(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)化方案三來介紹寫時拷貝(寫時復制)


方案三:是每個對象對應(yīng)一個整型空間(即_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++写时拷贝1的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。