您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“C++拷貝構造函數怎么使用”,內容詳細,步驟清晰,細節處理妥當,希望這篇“C++拷貝構造函數怎么使用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
只有單個形參,該形參是對本類類型對象的引用(一般常用const修飾),在用已存在的類類型對象創建新對象時由編譯器自動調用
拷貝構造函數是構造函數的一個重載,因此顯式的定義了拷貝構造,那么編譯器也不再默認生成構造函數。
拷貝構造也是一個特殊的成員函數
特征如下:
拷貝構造是構造函數的一個重載;
拷貝構造的參數只有一個并且類型必須是該類的引用,而不是使用傳值調用,否則會無限遞歸;
若沒有顯式定義拷貝構造函數,編譯器會自己生成一個默認拷貝構造,默認的拷貝構造函數對象按按內存存儲和字節序完成拷貝,也叫淺拷貝;
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
對于那些直接管理著內存資源的類(含有指針變量),那么簡單的值拷貝還頂得住嗎?顯然頂不住啊。
通過圖示說明:
兩個string類的對象指向了同一塊空間,這不就亂套了嗎,如果其中一個對象通過指針改變了指向內存的數據,那么另一個對象也會受到影響,這是我們不愿發生的,我們希望每個對象都能獨立運作。
下面這個程序會崩潰
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; }
原因是兩個string類的成員指針都指向一塊內存,而它們又分別調用了一次析構函數,相當于對同一塊內存空間釋放了兩次,程序崩潰。
因此對于這種情況的對象,我們就不能再使用編譯器生成的默認拷貝構造了,而只能自己去顯式的定義拷貝構造并且要實現深拷貝。
編譯器默認生成的拷貝構造會做些什么呢?
對于內置類型成員
完成值拷貝;
對于自定義類型成員
調用成員的拷貝構造;
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; }
如果默認生成的拷貝構造沒有調用Time類成員的拷貝構造,那么d2的_t
的值應該是(_hour = 0, _minute = 0, _second = 0),而這里最終的結果是d2中的_t
和d2中的_t
值相同。
這里Date類中自動生成的拷貝構造函數的內置類型會進行字節序拷貝,而對于自定義類型_t
調用了Time的拷貝構造函數。
拷貝構造是構造函數的一個重載,因此拷貝構造函數也是有初始化列表的,所以也建議在初始化列表階段完成對對象的初始化,養成良好習慣。
可以不顯式定義拷貝構造函數的情況
成員變量沒有指針;
成員有指針,但并沒有管理內存資源;
之前一直存在這個誤區:
我們都知道,編譯器生成的構造函數在初始化列表會調用成員的構造函數,而我們顯式去定義構造函數時,即使我們不寫也會在初始化列表去調用自定義類型成員的構造函數。
通過類比,我就犯了一個低級錯誤:
就是既然編譯器生成的拷貝構造可以在初始化列表自動調用自定義成員的拷貝構造,那么我們顯式定義的拷貝構造即使不寫,也會在初始化列表自動去調用自定義成員的拷貝構造。
于是我寫出了如下代碼:
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)//顯式定義了拷貝構造 { } 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; }
通過監視窗口查看d2調用拷貝構造后的值:
并沒有拷貝成功。
我只顧著類比它們的功能,可我恰恰忽略了拷貝構造也是一種構造函數啊,那么自然的初始化列表也是和普通構造一樣,會去調用自定義類的構造函數,不處理內置類型。只不過編譯器生成的是經過處理的構造函數達到了拷貝的效果。(太傻逼了這錯誤)
讀到這里,這篇“C++拷貝構造函數怎么使用”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。