首頁 > 軟體

C++的深淺拷貝和寫時拷貝你瞭解嗎

2022-03-23 13:01:36

1.淺拷貝

淺拷貝:對於有申請空間的物件的類來說,是按照位元組序依次拷貝過去的,並沒有另外申請一塊空間。因此,在呼叫解構函式的時候會造成同一塊空間釋放兩次的情況,從而使程式崩潰。

如下範例:

class string
{
public:
	string(const char* str)
	{
		//構造string類物件時,如果傳遞nullptr指標
		//認為程式非法,此處斷言下
		assert(str);
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	~string()
	{
		if (_str)
		{
			delete[] _str;
			_str = nullptr;
		}
	}
private:
	char* _str;
};
void test()
{
	string s1("linmanman");
	string s2(s1);
}

執行看看,報錯了

分析錯因:

淺拷貝是指向同一塊空間的,這樣就會有倆個無法避免的問題:

1.解構倆次空間,程式崩潰

2.其中一個的值被修改了,會影響到另外一個的值。

深拷貝:給每個物件單獨分配資源,就是給待拷貝的物件另開一片空間,再把原物件空間上的值拷貝過來,這樣在呼叫解構函式的時候就不會產生衝突。

2.深拷貝

傳統寫法的string類的深拷貝是自己開空間,自己將拷貝的物件拷貝到待拷貝物件中。

string(const string& s)
 : _str(new char[strlen(s._str)+1])
 {
 strcpy(_str, s._str);
 }
 string& operator=(const string& s)
 {
 if(this != &s)
 {
 char* pStr = new char[strlen(s._str) + 1];
 strcpy(pStr, s._str);
 delete[] _str;
 _str = pStr;
 }
 return *this;
 }

現代寫法的string類的深拷貝堪稱是“移花接木”

string(const string& s)
	:_str(nullptr)//必須置空,因為_str開始是個亂數,交換給tmp._str後,釋放會引起問題
{
	string tmp(s._str);//直接利用建構函式,給tmp物件開闢了一塊空間
	swap(tmp);
}
string& operator=(string s)
{
	swap(s);//這個swap是咱們自己寫的哦
	return *this;
}

順帶提一嘴,各個編譯器深拷貝的底層實現略有差異(當然邏輯是一樣的)

VS 2013下的深拷貝

g++下的深拷貝

3.參照計數+寫時拷貝

寫時拷貝就是一種拖延症, 是在淺拷貝的基礎之上增加了參照計數的方式來實現的。

參照計數:用來記錄資源使用者的個數。在構造時,將資源的計數給成1,每增加一個物件使用該資源,就給計數增加1,當某個物件被銷燬時,先給該計數減1,然後再檢查是否需要釋放資源,如果計數為1,說明該物件時資源的最後一個使用者, 將該資源釋放;否則就不能釋放,因為還有其他物件在使用該資源。

總結

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容! 


IT145.com E-mail:sddin#qq.com