您好,登錄后才能下訂單哦!
這期內容當中小編將會給大家帶來有關C++中有哪些拷貝方式,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
淺拷貝
淺拷貝就其他兩種而言就相當的簡單了,其編寫的代碼如下:
String::String(const String &s)
: _str(s._str)
, _size(s._size)
, _capacity(s._capacity)
{}
String & String::operator=(const String &s)
{
if (_str != s._str)
{
delete[] _str;
_str = s._str;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
雖然這種方法非常簡單,但這種方法存在很大的漏洞,這種方法拷貝構造以及賦值運算符的重載后的對象與被拷貝的對象指向同一塊空間,如果使用其中一個對象對其中的值進行修改時會導致另一個對象中的值也會發生改變,所以一般情況下不會使用淺拷貝這種拷貝方式。
但當對象中的值不能進行改變是一個const常量時,對象只可進行讀不能進行修改,使用淺拷貝可減少內存的開銷,不會有問題。
深拷貝
通過了解淺拷貝,我們發現淺拷貝的問題在于沒有新空間的開辟所導致不能對對象中的值進行改變。但我們大部分的對象中的值都會需要進行修改,所以深拷貝就是為了解決這問題而出現的。既然指向空間相同就不能隨意更改對象中內容,那么深拷貝就先給需要拷貝的對象開辟空間,再將內容拷貝過去。深拷貝分為傳統寫法以及現代寫法這兩種方法,其編寫的代碼如下:
class String
{
public:
String(const char* str = "");
String(const String& s);
String& operator=(const String& s);
~String();
private:
char* _str;
};
//傳統寫法完成String深拷貝
//String::String(const char* str)
// : _str(new char[strlen(str) + 1])
//{
// strcpy(_str, str);
//}
//
//String::String(const String& s) // 必須用引用否則會無限的遞歸
// : _str(new char[strlen(s._str) +1]) // 開辟s._str大小的空間,+1是因為‘/0’
//{
// strcpy(_str, s._str);
//}
//
//String& String::operator=(const String& s)
//{
// if (this != &s) // 判斷是否相同
// {
// if(_str)
// delete[] _str; // _str不為空,則需要先釋放空間
// _str = new char[strlen(s._str) + 1];
// strcpy(_str, s._str);
// }
// return *this;
//}
//
//String::~String()
//{
// if (NULL != _str)
// {
// delete[] _str;
// }
//}
//現代寫法完成String深拷貝
String::String(const char* str)
: _str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String::String(const String& s)
{
String tmp(s._str); // 用s._str定義一個tmp對象
swap(_str, tmp._str); // 將指針進行交換
}
String& String::operator=(String s) // 形參調用構造函數
{
swap(_str, s._str);
return *this;
}
String::~String()
{
if (NULL != _str)
{
delete[] _str;
}
}
相比較兩種方法而言,更推薦現代寫法,原因如下:
現代寫法代碼量相對而言較少,效率更高
現代寫法的賦值運算符重載中調用了拷貝構造、拷貝構造中調用了構造函數,故現代寫法中的復用性更強,更易于管理
傳統寫法中的賦值運算符重載會先釋放原先開辟的空間,這樣如果下面重新開辟空間失敗不能拷貝時,那么原有數據不會被保留
深拷貝雖然會再次開辟空間增加內存的占用,但這樣可以解決淺拷貝不能做到的問題。
寫時拷貝
其實通過上面兩種拷貝方式就可以解決很多問題,但淺拷貝不能修改,深拷貝如果不修改又占有了更多空間。因此寫時拷貝就出現了,其實寫時拷貝就是將上面兩種方法的優點進行結合,通過引入一個引用計數來解決這些問題。
寫時拷貝較上面方法更為復雜,所以在這里先進行說明:寫時拷貝需要一個引用計數,因此要增加一個存放一個引用計數的位置,我們將這個位置放在_str前面的四個字節,每當拷貝一次就將引用計數++一次,進行修改時先判斷一下引用計數是否為1,若為1則直接進行修改;否則先重新開辟一塊空間,將原有引用計數–,再進行修改。
下面簡單編寫一下其代碼:
class String
{
public:
String(char* str = "")
:_str(new char[(strlen(str) + 5)]) // 多開辟4個字節來存放引用計數
{
*((int*)_str) = 1;
_str += 4; // str中內容在引用計數后
strcpy(_str, str);
}
String(const String& s)
{
_str = s._str; // 基本與淺拷貝相似
++GetRefCount();
}
String& operator=(const String& s)
{
if (_str != s._str)
{
if (--(GetRefCount()) == 0)
{
delete[] _str;
}
_str = s._str;
++(GetRefCount());
}
return *this;
}
~String()
{
if (--(GetRefCount()) == 0)
{
delete[] (_str - 4);
}
}
int& GetRefCount() // 返回引用類型,也可寫成指針類型
{
return *((int*)(_str - 4));
}
private:
char* _str;
};
上述就是小編為大家分享的C++中有哪些拷貝方式了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。