詳解C++之類和對象(2)
一.構(gòu)造函數(shù)
1.構(gòu)造函數(shù)的定義:
構(gòu)造函數(shù) 是一個 特殊的成員函數(shù),名字與類名相同 , 創(chuàng)建類類型對象時由編譯器自動調(diào)用 ,保證每個數(shù)據(jù)成員都有 一個合適的初始值,并且 在對象的生命周期內(nèi)只調(diào)用一次 。 其實(shí)構(gòu)造函數(shù)的作用就是完成成員變量的初始化 ,但不同于c語言的初始化構(gòu)造函數(shù)可以實(shí)在創(chuàng)造對象的同時就完成成員變量的初始化。
2.構(gòu)造函數(shù)的特征:
1. 函數(shù)名與類名相同。
2. 無返回值。
3. 對象實(shí)例化時編譯器 自動調(diào)用 對應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載。
3.構(gòu)造函數(shù)的實(shí)現(xiàn):
構(gòu)造函數(shù)的實(shí)現(xiàn)主要有三種,
1.當(dāng)用戶沒有實(shí)現(xiàn)構(gòu)造函數(shù)的話系統(tǒng)會默認(rèn)創(chuàng)造一個,此時系統(tǒng)會將內(nèi)置類型的成員變量賦予隨機(jī)值,而對于自定義類型的成員變量則會調(diào)用他們的構(gòu)造函數(shù)。(注:內(nèi)置類型一般指的是:int char double float等這些定義好的類型,自定義類型指的是:struct這種類型以及class類這種)。
2.當(dāng)然用戶也可以自己實(shí)現(xiàn)構(gòu)造函數(shù),一種為無參構(gòu)造
3.類一種為帶參構(gòu)造,但是在帶參構(gòu)造中我們使用全缺省構(gòu)造。我們用代碼展示一下:
3.1.系統(tǒng)默認(rèn)的構(gòu)造函數(shù)
我們可以看到當(dāng)我們沒有在Data類進(jìn)行函數(shù)構(gòu)造的時系統(tǒng)將會自己默認(rèn)創(chuàng)建構(gòu)造函數(shù),對內(nèi)置類型變量賦予隨機(jī)值,自定義類型調(diào)用自己的構(gòu)造函數(shù)(若自定義類型也沒有定義構(gòu)造函數(shù)那么此例子中的_a1和_a2也會被賦予隨機(jī)值)
3.2無參構(gòu)造
3.3 帶參構(gòu)造
這里出一個問題對于代碼風(fēng)格造成的問題:成員變量year最后的結(jié)果是多少呢?
class A{public:A(int year){year = year;}private:int year;};int main(){A a(20);}
答案是:隨機(jī)值。那么為什么是隨機(jī)值呢?這里主要是變量之間它采用了就近原則,所以等式左邊的year會直接尋找離他最近的變量所以會將等式右邊的year直接賦值給它自己,所以year最后的值就是隨機(jī)值。
我們繼續(xù)來說帶參的構(gòu)造函數(shù),我們一般推薦使用的是全缺省的構(gòu)造函數(shù)(注:
無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個。無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認(rèn)生成的構(gòu)造函數(shù),三者都可以認(rèn)為是默認(rèn)成員函數(shù)。
)
二 析構(gòu)函數(shù)
構(gòu)造函數(shù)時完成對象的初始化,那么一個對象又是怎么樣被銷毀的呢?
1.析構(gòu)函數(shù)的定義
與構(gòu)造函數(shù)功能相反,析構(gòu)函數(shù)不是完成對象的銷毀,局部對象銷毀工作是由編譯器完成的。而 對象在銷毀時會自動調(diào)用析構(gòu)函數(shù),完成類的一些清理工作。
2.析構(gòu)函數(shù)的特征
1. 析構(gòu)函數(shù)名是在類名前加上字符 ~ 。
2. 無參數(shù)無返回值。
3. 一個類有且只有一個析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會自動生成默認(rèn)的析構(gòu)函數(shù) 。
4. 對象生命周期結(jié)束時, C++ 編譯系統(tǒng)系統(tǒng)自動調(diào)用析構(gòu)函數(shù)。
這里我們用棧的例子來說明析構(gòu)函數(shù)的實(shí)現(xiàn)以及作用。
class Stack { public: Stack(int capacity = 4) { _a = (int*)malloc(sizeof(int)*capacity); if (_a == nullptr) { cout << "malloc fail" << endl; exit(-1); } _top = 0; _capacity = capacity; } //析構(gòu)函數(shù)的實(shí)現(xiàn) ~Stack() { // 像Stack這樣的類,對象中的資源需要清理工作,就用析構(gòu)函數(shù) free(_a); _a = nullptr; _top = _capacity = 0; } private: int* _a; int _top; int _capacity; };
這里是完成構(gòu)造函數(shù),有自己定義的析構(gòu)函數(shù)的效果。同構(gòu)造函數(shù)一樣對于內(nèi)置成員變量析構(gòu)函數(shù)會置為隨機(jī)值,而自定義類型則會去調(diào)用他們的析構(gòu)函數(shù)。
三 拷貝函數(shù)
如果某些時候我們需要去復(fù)制一個對象,這樣的話我們該怎么樣去解決呢?
這里我們就需要引入拷貝函數(shù)。那么什么叫做拷貝函數(shù)呢?我們應(yīng)該去怎么實(shí)現(xiàn)呢?有什么注意事項(xiàng)呢?這里我們一一來說道。
1.拷貝函數(shù)定義
構(gòu)造函數(shù) : 只有單個形參 ,該形參是對本 類類型對象的引用 ( 一般常用 const 修飾 ) ,在用 已存在的類類型對象 創(chuàng)建新對象時由編譯器自動調(diào)用 。
2.拷貝函數(shù)的特性
1. 拷貝構(gòu)造函數(shù) 是構(gòu)造函數(shù)的一個重載形式 。
2. 拷貝構(gòu)造函數(shù)的參數(shù)只有一個且必須使用引用傳參,使用傳值方式會引發(fā)無窮遞歸調(diào)用 。
3. 若未顯示定義,系統(tǒng)生成默認(rèn)的拷貝構(gòu)造函數(shù)。 默認(rèn)的拷貝構(gòu)造函數(shù)對象按內(nèi)存存儲按字節(jié)序完成拷 貝,這種拷貝我們叫做淺拷貝,或者值拷貝。
3.拷貝函數(shù)的實(shí)現(xiàn)
拷貝函數(shù)的實(shí)現(xiàn)分為兩種一種是系統(tǒng)默認(rèn),一種是自己定義。我們分別來看其效果
class A { public: A() { _a1 = 1; _a2 = 2; } ~A() { cout << "A()" << endl; } private: int _a1; int _a2; }; class Data { public: /*Data() { _year = 2021; _month = 12; _day = 12; }*/ //Data(int year, int month, int day) //{ // _year = year; // _month = month; // _day = day; //} Data(int year = 2022, int month = 12, int day = 12) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; A a; }; int main() { Data s; //拷貝函數(shù)的調(diào)用 Data s2(s); return 0; }
調(diào)用系統(tǒng)默認(rèn)生成拷貝函數(shù)(注:這里拷貝函數(shù)的拷貝對自定義類型和內(nèi)置類型的成員變量處理都是一致的完成字節(jié)序的值拷貝)
圖1 調(diào)用系統(tǒng)默認(rèn)生成的拷貝函數(shù)
圖2 調(diào)用用戶自己定義的拷貝函數(shù)
在這里我們順便說一下在自定義拷貝函數(shù)的時候一定要使用引用不然會出現(xiàn)無限遞歸例如 Data(Data s){}正確的使用是Data (const Data & s){}其中const是為了保護(hù)原數(shù)據(jù)不被輕易改動。
class A { public: A() { _a1 = 1; _a2 = 2; } ~A() { cout << "A()" << endl; } private: int _a1; int _a2; }; class Data { public: /*Data() { _year = 2021; _month = 12; _day = 12; }*/ //Data(int year, int month, int day) //{ // _year = year; // _month = month; // _day = day; //} Data( const Data &s) { _year = s._year; _month = s._month; _day = s._day; } Data(int year = 2023, int month = 12, int day = 12) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; A a; }; int main() { Data s; //拷貝函數(shù)的調(diào)用 Data s2(s); return 0; }
我們可以發(fā)現(xiàn)s2均完整的賦值了s的內(nèi)容,但是這里真的就沒有問題了嗎?如果我們使用系統(tǒng)默認(rèn)生成的拷貝函數(shù)成員變量中含有指針那么會出現(xiàn)什么樣的問題呢?
class String { public: String(const char* str = "jack") { _str = (char*)malloc(strlen(str) + 1); strcpy(_str, str); } ~String() { cout << "~String()" << endl; free(_str); } private: char* _str; }; int main() { String s; String s1(s); }
我們可以看到雖然雖然s1拷貝了s的內(nèi)容但是最后系統(tǒng)還是拋出了錯誤那么這個錯誤來自那里呢?
我們看這幅圖
這里就是我們之前說的系統(tǒng)默認(rèn)生成的拷貝函數(shù)是淺拷貝,那么怎么去完成深拷貝我們后邊在繼續(xù)講解。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C++中內(nèi)存池的簡單原理及實(shí)現(xiàn)詳解
內(nèi)存池的思想是,在真正使用內(nèi)存之前,預(yù)先申請分配一定數(shù)量、大小預(yù)設(shè)的內(nèi)存塊留作備用。本文主要來和大家聊聊內(nèi)存池的簡單原理及實(shí)現(xiàn),希望對大家有所幫助2023-03-03深入解析C++編程中__alignof 與__uuidof運(yùn)算符的使用
這篇文章主要介紹了C++編程中__alignof 與__uuidof運(yùn)算符的使用,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2016-01-01C++中的多態(tài)與虛函數(shù)的內(nèi)部實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄狢++中的多態(tài)與虛函數(shù)的內(nèi)部實(shí)現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12vscode調(diào)試gstreamer源碼的詳細(xì)流程
在本文中主要介紹了如何使用vscode調(diào)試C++和python程序,并進(jìn)一步分析了如何調(diào)試gstreamer源碼,講述了如何調(diào)試gstreamer源碼的具體流程,感興趣的朋友跟隨小編一起看看吧2023-01-01C/C++ 中堆和棧及靜態(tài)數(shù)據(jù)區(qū)詳解
這篇文章主要介紹了C/C++ 中堆和棧及靜態(tài)數(shù)據(jù)區(qū)詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04