首頁 > 軟體

C++深淺拷貝及簡易string類實現方式

2023-02-06 06:00:50

三種拷貝方式

淺拷貝

對於自定義的string類,如果不顯式定義拷貝建構函式,編譯器會預設生成拷貝建構函式,此時的拷貝方式是淺拷貝,兩個物件會公用一塊兒記憶體,解構時同一空間被釋放兩次,會導致程式崩潰。

賦值運運算元過載也會產生同樣的問題,同時,由於被賦值物件原來有空間,淺拷貝還會導致舊的空間無法找到,造成記憶體漏失。

深拷貝

類中設計到資源的管理,拷貝建構函式、賦值運運算元過載以及解構函式都要顯示給出,按照深拷貝的方式。

深拷貝的方式讓每個物件都獨立擁有一份資源,不會造成多次釋放導致程式崩潰的問題。

寫時拷貝

寫時拷貝是通過淺拷貝+參照計數的方式來實現的,參照計數是用來記錄資源的被參照的次數,

可以將這種寫時拷貝的機制想象成“拖延症”,只有當不得不進行拷貝時,才會開闢新空間進行拷貝

VS與GCC中的拷貝方式

Windows VS2022

VS中採用的是深拷貝的方式

Linux GCC

GCC編譯器採用的是寫時拷貝的方式

簡易string類

簡易string類主要實現四個功能,即建構函式、拷貝建構函式、解構函式、賦值運運算元過載,主要考察深淺拷貝

實現簡易string類有兩種程式碼風格,一種傳統版寫法,程式碼複用性第,可讀性較好;另一種稱為現代版寫法,程式碼複用性高,但是較難理解。

傳統版寫法的string類

建構函式

步驟:

  • 判斷是否為空指標,string類不允許nullptr構造物件
  • 申請新空間
  • 將字串中的值拷貝到申請的空間
string(const char* str = "")
{
	if (nullptr == str)
	{
		assert(false);
		return;
	}
	//+1是因為有'',strcpy會將源字串中的''拷貝到目標空間
	_str = new char[strlen(str) + 1];
	strcpy(_str, str);
}

拷貝建構函式

步驟:

  • 開闢空間
  • 用源物件的_str給當前物件的_str賦值
string(const string& s)
	:_str(new char[strlen(s._str) + 1])
{
	strcpy(_str, s._str);
}

賦值運運算元過載

步驟:

  • 判斷是否自己給自己賦值
  • 開闢新空間
  • 拷貝元素
  • 刪除舊空間
string& operator=(const string& s)
{
	//避免自己給自己賦值
	if (this != &s)
	{
		char* temp = new char[strlen(s._str) + 1];
		strcpy(temp, s._str);
		delete[] _str;
		_str = temp;
	}
	return *this;
}

另一種寫法

這種寫法不用定義臨時變數,程式碼相對簡潔一點,但是如果new申請空間失敗,舊的空間也無法找到。

解構函式

步驟:

  • 釋放空間
  • 將指標置為空
~string()
{
    if (_str)
    {
        delete[]_str;
        _str = nullptr;
    }
}

 

現代版寫法string類

建構函式

string(const char* str = "")
{
	if (str == nullptr)
	{
		assert(false);
	}
	_str = new char[strlen(str) + 1];
	strcpy(_str, str);
}

拷貝建構函式

拷貝建構函式中利用建構函式,實現了程式碼的複用

步驟:

  • 在初始化列表中將_str置為空
  • 定義一個臨時的string類物件,指向要拷貝的物件相同位置
  • 交換臨時物件與當前物件的_str
string(const string& s)
	:_str(nullptr)
{
	//呼叫建構函式
	string temp(s._str);
	//交換以後temp指向空,函數退出後被銷燬
	swap(_str, temp._str);
}

賦值運運算元過載函數

步驟:

  • 判斷是否為自己給自己賦值
  • 呼叫拷貝建構函式定義臨時變數
  • 交換臨時變數與當前物件的_str
string& operator=(string& s)
{
	if (this != &s)
	{
		string temp(s);
		swap(_str, s._str);
	}
	return *this;
}

更簡潔的寫法:

string& operator=(string s)
{
	//傳參呼叫拷貝建構函式,不用判斷是否給自己賦值
	swap(_str, s._str);
	return *this;
}

解構函式

~string()
{
	if (_str)
	{
		delete[] _str;
		_str = nullptr;
	}
}

總結

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


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