掌握C++:揭秘寫(xiě)時(shí)拷貝與淺深拷貝之間的關(guān)系
1. 經(jīng)典的string類(lèi)問(wèn)題
上一篇博客已經(jīng)對(duì)string類(lèi)進(jìn)行了簡(jiǎn)單的介紹,大家只要能夠正常使用即可。
在面試中,面試官總喜歡讓學(xué)生自己來(lái)模擬實(shí)現(xiàn)string類(lèi),最主要是實(shí)現(xiàn)string類(lèi)的構(gòu)造、拷貝構(gòu)造、賦值運(yùn)算符重載以及析構(gòu)函數(shù)。大家看下以下string類(lèi)的實(shí)現(xiàn)是否有問(wèn)題?
// 為了和標(biāo)準(zhǔn)庫(kù)區(qū)分,此處使用String
class String
{
public:
//String()
// :_str(new char[1])
//{
// *_str = '\0';
//}
//String(const char* str = "\0") // 錯(cuò)誤示范
//String(const char* str = nullptr) // 錯(cuò)誤示范
String(const char* str = "")
{
// 構(gòu)造String類(lèi)對(duì)象時(shí),如果傳遞nullptr指針,可以認(rèn)為程序非法
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
// 測(cè)試
void TestString()
{
String s1("hello string");
String s2(s1);
}

說(shuō)明:上述String類(lèi)沒(méi)有顯式定義其拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載,此時(shí)編譯器會(huì)合成默認(rèn)的,當(dāng)用s1構(gòu)造s2時(shí),編譯器會(huì)調(diào)用默認(rèn)的拷貝構(gòu)造。最終導(dǎo)致的問(wèn)題時(shí),s1、s2共用同一塊內(nèi)存空間,在釋放時(shí)同一塊空間被釋放多次而引起程序崩潰,這種拷貝方式,稱(chēng)為淺拷貝。
2. 淺拷貝
淺拷貝:也稱(chēng)位拷貝,編譯器只是將對(duì)象中的值拷貝過(guò)來(lái)。如果對(duì)象中管理資源,最后就會(huì)導(dǎo)致多個(gè)對(duì)象共享同一份資源,當(dāng)一個(gè)對(duì)象銷(xiāo)毀時(shí)就會(huì)將該資源釋放掉,而此時(shí)另一些對(duì)象不知道該資源已經(jīng)被釋放,以為還有效,所以當(dāng)繼續(xù)對(duì)資源進(jìn)行操作時(shí),就會(huì)發(fā)生訪問(wèn)違規(guī)。
就像一個(gè)家庭中有兩個(gè)孩子,但父母只買(mǎi)了一份玩具,兩個(gè)孩子愿意一塊玩,則萬(wàn)事大吉,萬(wàn)一不想分享就你爭(zhēng)我?jiàn)Z,玩具損壞。

可以采用深拷貝解決淺拷貝問(wèn)題,即:每個(gè)對(duì)象都有一份獨(dú)立的資源,不要和其他對(duì)象共享。父母給每個(gè)孩子都買(mǎi)一份玩具,各自玩各自的就不會(huì)有問(wèn)題了。

3. 深拷貝
如果一個(gè)類(lèi)中涉及到資源的管理,其拷貝構(gòu)造函數(shù),賦值運(yùn)算符重載以及析構(gòu)函數(shù)必須要顯式給出。一般情況都是按照深拷貝方式提供。
3.1 傳統(tǒng)寫(xiě)法的String類(lèi)
class String
{
public:
String(const char* str = "")
{
// 構(gòu)造String類(lèi)對(duì)象時(shí),如果傳遞nullptr指針,可以認(rèn)為程序非法
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_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* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
}
return *this;
}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
3.2 現(xiàn)代寫(xiě)法的String類(lèi)
class String
{
public:
String(const char* str = "")
{
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
String(const String& s)
: _str(nullptr)
{
String strTmp(s._str);
swap(_str, strTmp._str);
}
// 對(duì)比下和上面的賦值那個(gè)實(shí)現(xiàn)比較好?
String& operator=(String s)
{
swap(_str, s._str);
return *this;
}
//String& operator=(const String& s)
//{
// if (this != &s)
// {
// String strTmp(s);
// swap(_str, strTmp._str);
// }
// return *this;
//}
~String()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
傳統(tǒng)寫(xiě)法就是老老實(shí)實(shí)自己開(kāi)空間、自己拷貝數(shù)據(jù)、自己刪除舊空間;而現(xiàn)代寫(xiě)法則利用了swap()函數(shù)以及局部變量出作用域自動(dòng)銷(xiāo)毀的特性,讓函數(shù)和編譯器幫我們“打工”,我們只要坐享其成即可。這兩種方式在效率上并沒(méi)有什么區(qū)別,只是讓代碼看起來(lái)更簡(jiǎn)潔,但這又會(huì)使代碼的可讀性降低??傮w來(lái)說(shuō),我還是更偏向于傳統(tǒng)寫(xiě)法。
4. 寫(xiě)時(shí)拷貝

寫(xiě)時(shí)拷貝就是一種拖延癥,是在淺拷貝的基礎(chǔ)之上增加了引用計(jì)數(shù)的方式來(lái)實(shí)現(xiàn)的。
引用計(jì)數(shù):用來(lái)記錄資源使用者的個(gè)數(shù)。在構(gòu)造時(shí),將資源的計(jì)數(shù)給成1,每增加一個(gè)對(duì)象使用該資源,就給計(jì)數(shù)增加1,當(dāng)某個(gè)對(duì)象被銷(xiāo)毀時(shí),先給該計(jì)數(shù)-1,然后再檢查是否需要釋放資源,如果計(jì)數(shù)為1,說(shuō)明該對(duì)象是資源的最后一個(gè)使用者,將該資源釋放;否則就不能釋放,因?yàn)檫€有其他對(duì)象也在使用該資源。
到此這篇關(guān)于掌握C++:揭秘寫(xiě)時(shí)拷貝與淺深拷貝之間的關(guān)系的文章就介紹到這了,更多相關(guān)C++ 淺拷貝 深拷貝 寫(xiě)時(shí)拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++ vector模擬實(shí)現(xiàn)的全過(guò)程
這篇文章主要給大家介紹了關(guān)于c++ vector的模擬實(shí)現(xiàn)過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
淺談C語(yǔ)言中include""與include<>的區(qū)別
C語(yǔ)言中包含文件有兩種包含符號(hào),一個(gè)是<>尖括號(hào),另一個(gè)是""雙引號(hào)。那么這兩個(gè)有什么區(qū)別呢?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2021-06-06
C++實(shí)現(xiàn)順序表的常用操作(插入刪出查找輸出)
實(shí)現(xiàn)順序表的插入,刪除,查找,輸出操作在C語(yǔ)言中經(jīng)常用到。下面小編給大家整理實(shí)現(xiàn)代碼,一起看下吧2016-08-08
C語(yǔ)言之實(shí)現(xiàn)單鏈表指定結(jié)點(diǎn)的插入方式
這篇文章主要介紹了C語(yǔ)言之實(shí)現(xiàn)單鏈表指定結(jié)點(diǎn)的插入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Qt+Quick實(shí)現(xiàn)圖片演示器的開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了Qt如何利用Quick實(shí)現(xiàn)圖片演示器的開(kāi)發(fā),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Qt有一定的幫助,需要的可以參考一下2023-01-01

