C++超詳細(xì)講解拷貝構(gòu)造函數(shù)
構(gòu)造函數(shù)
只有單個(gè)形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創(chuàng)建新對象時(shí)由編譯器自動(dòng)調(diào)用
拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載,因此顯式的定義了拷貝構(gòu)造,那么編譯器也不再默認(rèn)生成構(gòu)造函數(shù)。
特征
拷貝構(gòu)造也是一個(gè)特殊的成員函數(shù)
特征如下:
- 拷貝構(gòu)造是構(gòu)造函數(shù)的一個(gè)重載;
- 拷貝構(gòu)造的參數(shù)只有一個(gè)并且類型必須是該類的引用,而不是使用傳值調(diào)用,否則會無限遞歸;
- 若沒有顯式定義拷貝構(gòu)造函數(shù),編譯器會自己生成一個(gè)默認(rèn)拷貝構(gòu)造,默認(rèn)的拷貝構(gòu)造函數(shù)對象按按內(nèi)存存儲和字節(jié)序完成拷貝,也叫淺拷貝;
class Date { public: Date(int year, int month, int day) : _year(year), _month(month), _day(day) {} void Display() { cout << _year << "-" << _month << "-" << _day << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2001, 7, 28); Date d2(d1); d1.Display(); d2.Display(); return 0; }
輸出:
2001-7-28
2001-7-28
對于那些直接管理著內(nèi)存資源的類(含有指針變量),那么簡單的值拷貝還頂?shù)米??顯然頂不住啊。
通過圖示說明:
兩個(gè)string類的對象指向了同一塊空間,這不就亂套了嗎,如果其中一個(gè)對象通過指針改變了指向內(nèi)存的數(shù)據(jù),那么另一個(gè)對象也會受到影響,這是我們不愿發(fā)生的,我們希望每個(gè)對象都能獨(dú)立運(yùn)作。
下面這個(gè)程序會崩潰
class String { public: String(const char* str = "songxin") { cout << "String(const char* str = \"songxin\")" << endl; _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~String()" << endl; free(_str); _str = nullptr; } private: char* _str; }; int main() { String s1; String s2(s1); return 0; }
原因是兩個(gè)string類的成員指針都指向一塊內(nèi)存,而它們又分別調(diào)用了一次析構(gòu)函數(shù),相當(dāng)于對同一塊內(nèi)存空間釋放了兩次,程序崩潰。
因此對于這種情況的對象,我們就不能再使用編譯器生成的默認(rèn)拷貝構(gòu)造了,而只能自己去顯式的定義拷貝構(gòu)造并且要實(shí)現(xiàn)深拷貝。
編譯器生成的拷貝構(gòu)造
編譯器默認(rèn)生成的拷貝構(gòu)造會做些什么呢?
- 對于內(nèi)置類型成員
? 完成值拷貝;
- 對于自定義類型成員
調(diào)用成員的拷貝構(gòu)造;
class Time { public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour), _minute(minute), _second(second) {} Time(Time& t) { _hour = t._hour; _minute = t._minute; _second = t._second; } private: int _hour; int _minute; int _second; }; Time top(0, 1, 1); class Date { public: Date(int year = 1900, int month = 1, int day = 1, Time& t = top) : _year(year), _month(month), _day(day), _t(t) {} private: int _year; int _month; int _day; Time _t; }; int main() { Time t(1, 1, 1); Date d1(2001, 7, 28,t); Date d2(d1); return 0; }
如果默認(rèn)生成的拷貝構(gòu)造沒有調(diào)用Time類成員的拷貝構(gòu)造,那么d2的_t
的值應(yīng)該是(_hour = 0, _minute = 0, _second = 0),而這里最終的結(jié)果是d2中的_t
和d2中的_t
值相同。
這里Date類中自動(dòng)生成的拷貝構(gòu)造函數(shù)的內(nèi)置類型會進(jìn)行字節(jié)序拷貝,而對于自定義類型_t
調(diào)用了Time的拷貝構(gòu)造函數(shù)。
拷貝構(gòu)造的初始化列表
拷貝構(gòu)造是構(gòu)造函數(shù)的一個(gè)重載,因此拷貝構(gòu)造函數(shù)也是有初始化列表的,所以也建議在初始化列表階段完成對對象的初始化,養(yǎng)成良好習(xí)慣。
可以不顯式定義拷貝構(gòu)造函數(shù)的情況
- 成員變量沒有指針;
- 成員有指針,但并沒有管理內(nèi)存資源;
顯式定義拷貝構(gòu)造的誤區(qū)
之前一直存在這個(gè)誤區(qū):
我們都知道,編譯器生成的構(gòu)造函數(shù)在初始化列表會調(diào)用成員的構(gòu)造函數(shù),而我們顯式去定義構(gòu)造函數(shù)時(shí),即使我們不寫也會在初始化列表去調(diào)用自定義類型成員的構(gòu)造函數(shù)。
通過類比,我就犯了一個(gè)低級錯(cuò)誤:
就是既然編譯器生成的拷貝構(gòu)造可以在初始化列表自動(dòng)調(diào)用自定義成員的拷貝構(gòu)造,那么我們顯式定義的拷貝構(gòu)造即使不寫,也會在初始化列表自動(dòng)去調(diào)用自定義成員的拷貝構(gòu)造。
于是我寫出了如下代碼:
class Time { public: Time(int hour = 0, int minute = 0, int second = 0) : _hour(hour), _minute(minute), _second(second) {} Time(Time& t) { _hour = t._hour; _minute = t._minute; _second = t._second; } private: int _hour; int _minute; int _second; }; Time top(2, 2, 2); class Date { public: Date(int year = 1900, int month = 1, int day = 1, Time& t = top) : _year(year), _month(month), _day(day), _t(t) {} Date(Date& d)//顯式定義了拷貝構(gòu)造 { } private: int _year; int _month; int _day; Time _t; }; int main() { Time t(1, 1, 1); Date d1(2001, 7, 28,t); Date d2(d1); return 0; }
通過監(jiān)視窗口查看d2調(diào)用拷貝構(gòu)造后的值:
并沒有拷貝成功。
我只顧著類比它們的功能,可我恰恰忽略了拷貝構(gòu)造也是一種構(gòu)造函數(shù)啊,那么自然的初始化列表也是和普通構(gòu)造一樣,會去調(diào)用自定義類的構(gòu)造函數(shù),不處理內(nèi)置類型。只不過編譯器生成的是經(jīng)過處理的構(gòu)造函數(shù)達(dá)到了拷貝的效果。(太傻逼了這錯(cuò)誤)
結(jié)論
拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一種,它也有初始化列表,如果是編譯器生成的拷貝構(gòu)造,它會對內(nèi)置類型做字節(jié)序拷貝,對自定義類型成員會調(diào)用自定義成員的拷貝構(gòu)造。
可如果是我們顯式定義出的拷貝構(gòu)造,它也是有初始化列表的,但是它的初始化列表可不會去調(diào)用成員的拷貝構(gòu)造奧,而是和普通構(gòu)造函數(shù)一樣,對于內(nèi)置類型成員不去初始化值,對于自定義類型成員調(diào)用自定義成員的構(gòu)造函數(shù)而不是拷貝構(gòu)造函數(shù)。
到此這篇關(guān)于C++超詳細(xì)講解拷貝構(gòu)造函數(shù)的文章就介紹到這了,更多相關(guān)C++拷貝構(gòu)造函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實(shí)現(xiàn)圖書管理系統(tǒng)源碼
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)圖書管理系統(tǒng)源碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03C++實(shí)現(xiàn)簡單通訊錄管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡單通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02C++核心編程之占位參數(shù)和默認(rèn)參數(shù)
這篇文章主要介紹了C++核心編程之占位參數(shù)和默認(rèn)參數(shù),c++中函數(shù)的形參列表中的形參是可以有默認(rèn)值的,函數(shù)的形參列表里可以有占位參數(shù),用來占位,調(diào)用函數(shù)時(shí)必須填補(bǔ)位置。下面更多相關(guān)內(nèi)容的詳細(xì)介紹,需要的小伙伴可以參考一下2022-03-03