String引用技術寫時拷貝

2021-08-15 16:07:44 字數 2227 閱讀 8327

string類的賦值淺拷貝會使多個物件指向同一塊空間,當呼叫析構函式時會使一塊空間釋放多次,導致程式崩潰。再進一步我們會想到深拷貝,呼叫拷貝構造或賦值時會拷貝一塊新的空間,並將值拷貝下來,這樣各自指向自己的資料塊,析構時釋放各自的資料塊。但由於不斷的開闢空間、釋放空間會花費時間,而且當建立物件較多時,會占用大量的記憶體。那怎樣去避免這樣的問題呢?

我們知道string類的淺拷貝使多個物件指向一塊空間 ,為了讓析構時這塊空間不被釋放多次,我們可以在這塊空間上加上引用技術(表示此時有幾個物件指向這塊空間),當我需要寫時才去開闢新的空間。這就是引用技術。

寫時拷貝技術可以理解為「寫的時候才去分配空間」,這實際上是一種拖延戰術。

原理:

寫時拷貝技術是通過"引用計數"實現的,在分配空間的時候多分配4個位元組,用來記錄有多少個指標指向塊空間,當有新的指標指向這塊空間時,引用計數加一,當要釋放這塊空間時,引用計數減一(假裝釋放),直到引用計數減為0時才真的釋放掉這塊空間。當有的指標要改變這塊空間的值時,再為這個指標分配自己的空間(注意這時引用計數的變化,舊的空間的引用計數減一,新分配的空間引用計數加一)。

此時我們知道要使用寫時拷貝技術實現string類需要加上引用技術,那我們又如何新增引用技術呢?   

如圖,三種方案參考,那種合理呢?

分析:

方案1:_refcount存放於string中,那麼每個例項中都有乙個不能實現共享。

方案2:_refcount為靜態成員,所用的string共享,不由類的建構函式初始化,而物件的建立需要呼叫建構函式,所以它無法計數到正在使用同一塊空間的物件的個數。

方案3:_refcount為指標型別,當然可以實現共享(當維護這塊空間不同),進一步我們可以考慮將_refcount放在_str開始或結束的四個位元組中。(放在開頭位置,增容不需要移動,如果放在結束位置的話,增容時需要移動計數的位置,所以這裡將介紹放於頭部的寫法)

string的實現

方法一:

class string

//拷貝構造

//s2(s1)

string(string& s)

//s1 = s2

string& operator=(const string& s)

return *this;

} void copyonwrite() }

char& operator(size_t index)

~string()

void release() }

char* c_str()

private:

char* _str;

int* _refcount;//引用的次數(引用技術) 創一塊空間配乙個引用技術

由結果可以看出,s1,s2,s3指向同一塊空間。

測試2:

當對s3進行寫時,s3的位址改變(對其進行拷貝)

方法二:

class string

string(string& s)

:_str(s._str )

~string()

string& operator= (const string& s)

return *this;

} void copyonwrite() }

char& operator(size_t index)

int& getrefcount()

void release() }

char* c_str()

private:

char* _str;

};

這裡不再對方法二進行測試

寫時拷貝技術

cow技術初窺 在linux程式中,fork 會產生乙個和父程序完全相同的子程序,但子程序在此後多會exec系統呼叫,出於效率考慮,linux中引入了 寫時複製 技術,也就是只有程序空間的各段的內容要發生變化時,才會將父程序的內容複製乙份給子程序。那麼子程序的物理空間沒有 怎麼去取指令執行exec系...

寫時拷貝技術

寫時拷貝故名思意 是在寫的時候 即改變字串的時候 才會真正的開闢空間拷貝 深拷貝 如果只是對資料的讀時,只會對資料進行淺拷貝 寫時拷貝技術是通過 引用計數 實現的,在分配空間的時候多分配4個位元組,用來記錄有多少個指標指向塊空間,當有新的指標指向這塊空間時,引用計數加一,當要釋放這塊空間時,引用計數...

寫時拷貝技術

寫時拷貝的主要思想是在複製原來實體的時候,如果複製後的實體不需要進行改變,那麼實際上沒必要進行實體內容的拷貝,只需建立乙個引用指向原來的物理記憶體,直接應用原來的實體內容即可,只有當需要對複製後的實體進行修改的時候才進行內容的拷貝。寫時拷貝技術在很多方面都有應用,典型的有 c 中的寫時拷貝技術 li...