前提 在對象拷貝過程中,如果沒有自定義拷貝構(gòu)造函數(shù),系統(tǒng)會提供一個缺省的拷貝構(gòu)造函數(shù),缺省的拷貝構(gòu)造函數(shù)對于基本類型的成員變量,按字節(jié)復(fù)制,對于類類型成員變量,調(diào)用其相應(yīng)類型的拷貝構(gòu)造函數(shù)。 閱讀《高質(zhì)量的c c++編程》,第9章有這樣一段話,類似的話在《c++primer》《effective C++》都有所提及,那就是拷貝構(gòu)造函數(shù)問題,這個是類編寫者的一個基礎(chǔ)問題。 位拷貝(淺拷貝)舉例,a指向b,b的改變其實(shí)會影響a的改變,同時a原本指向的空間發(fā)生泄漏。 然后這種情況下有了深拷貝。 我對其繪制思維導(dǎo)圖,方便閱讀并用分點(diǎn)的方式進(jìn)行總結(jié); 何時調(diào)用? 以下情況都會調(diào)用拷貝構(gòu)造函數(shù): 一個對象以值傳遞的方式傳入函數(shù)體 一個對象以值傳遞的方式從函數(shù)返回 一個對象需要通過另外一個對象進(jìn)行初始化。 淺拷貝:位拷貝,拷貝構(gòu)造函數(shù),賦值重載 多個對象共用同一塊資源,同一塊資源釋放多次,崩潰或者內(nèi)存泄漏 深拷貝:每個對象共同擁有自己的資源,必須顯式提供拷貝構(gòu)造函數(shù)和賦值運(yùn)算符。 缺省拷貝構(gòu)造函數(shù)在拷貝過程中是按字節(jié)復(fù)制的,對于指針型成員變量只復(fù)制指針本身,而不復(fù)制指針?biāo)赶虻哪繕?biāo)--淺拷貝。 我們用自己編寫的string舉例class String{public: const char* c_str() { return _str; } String(const char* str = '') :_str(new char[strlen(str) + 1]) { strcpy(_str, str); } String(const String &s) :_str(NULL) { String tmp(s._str); swap(_str, tmp._str); } ~String() { if (_str) { delete[]_str; } }private: char* _str;}; 通過開辟空間的方式,進(jìn)行深拷貝 String s1('字符串1'); String s2(s1); cout << s2.c_str() << endl; 拷貝成功; 這種方式采取的 拷貝構(gòu)造,注意這個 String(const String &s) :_str(NULL) { String tmp(s._str); swap(_str, tmp._str); } 代碼解析:其中this指向拷貝的對象,s指向試圖拷貝的原對象。(測試中的 this指向s2,s指向s1) 其中利用構(gòu)造函數(shù)開辟空間,建立臨時的tmp,然后進(jìn)行交換完成拷貝。 當(dāng)然,我們也可以使用賦值操作符重載完成這一功能(如例子s1=s2) String& operator =(const String& s) { if (this != &s) { String tmp(s._str); swap(tmp._str, _str); return *this; } }//調(diào)用構(gòu)造析構(gòu) //本代碼是tmp調(diào)用的構(gòu)造函數(shù) String(const char* str = '') :_str(new char[strlen(str) + 1]) { strcpy(_str, str); }/*String tmp(s._str)調(diào)用這個構(gòu)造函數(shù),開辟空間,建立一個和s1一樣大小的空間,并拷貝值*/ 代碼解析: s1(this),s2(s) 建立tmp,tmp有和s2一樣大的空間,一樣的數(shù)值(調(diào)用構(gòu)造函數(shù)),然后交換使s1(this)指向2號空間,獲得拷貝,tmp指向3號空間,tmp生命周期結(jié)束調(diào)用析構(gòu)函數(shù)釋放,功能完成。 當(dāng)然 賦值重載函數(shù)可以寫的更加簡潔 String &operator=(String s) { swap(_str, s._str); return *this; } 利用tmp的方式是 借助構(gòu)造函數(shù),這一種方式則是借助拷貝構(gòu)造函數(shù) |
|