詳解C++之類和對象(2)
一.構(gòu)造函數(shù)
1.構(gòu)造函數(shù)的定義:
構(gòu)造函數(shù) 是一個 特殊的成員函數(shù),名字與類名相同 , 創(chuàng)建類類型對象時由編譯器自動調(diào)用 ,保證每個數(shù)據(jù)成員都有 一個合適的初始值,并且 在對象的生命周期內(nèi)只調(diào)用一次 。 其實構(gòu)造函數(shù)的作用就是完成成員變量的初始化 ,但不同于c語言的初始化構(gòu)造函數(shù)可以實在創(chuàng)造對象的同時就完成成員變量的初始化。
2.構(gòu)造函數(shù)的特征:
1. 函數(shù)名與類名相同。
2. 無返回值。
3. 對象實例化時編譯器 自動調(diào)用 對應(yīng)的構(gòu)造函數(shù)。
4. 構(gòu)造函數(shù)可以重載。
3.構(gòu)造函數(shù)的實現(xiàn):
構(gòu)造函數(shù)的實現(xiàn)主要有三種,
1.當用戶沒有實現(xiàn)構(gòu)造函數(shù)的話系統(tǒng)會默認創(chuàng)造一個,此時系統(tǒng)會將內(nèi)置類型的成員變量賦予隨機值,而對于自定義類型的成員變量則會調(diào)用他們的構(gòu)造函數(shù)。(注:內(nèi)置類型一般指的是:int char double float等這些定義好的類型,自定義類型指的是:struct這種類型以及class類這種)。
2.當然用戶也可以自己實現(xiàn)構(gòu)造函數(shù),一種為無參構(gòu)造
3.類一種為帶參構(gòu)造,但是在帶參構(gòu)造中我們使用全缺省構(gòu)造。我們用代碼展示一下:
3.1.系統(tǒng)默認的構(gòu)造函數(shù)
我們可以看到當我們沒有在Data類進行函數(shù)構(gòu)造的時系統(tǒng)將會自己默認創(chuàng)建構(gòu)造函數(shù),對內(nèi)置類型變量賦予隨機值,自定義類型調(diào)用自己的構(gòu)造函數(shù)(若自定義類型也沒有定義構(gòu)造函數(shù)那么此例子中的_a1和_a2也會被賦予隨機值)


3.2無參構(gòu)造


3.3 帶參構(gòu)造
這里出一個問題對于代碼風格造成的問題:成員變量year最后的結(jié)果是多少呢?
class A{public:A(int year){year = year;}private:int year;};int main(){A a(20);}
答案是:隨機值。那么為什么是隨機值呢?這里主要是變量之間它采用了就近原則,所以等式左邊的year會直接尋找離他最近的變量所以會將等式右邊的year直接賦值給它自己,所以year最后的值就是隨機值。

我們繼續(xù)來說帶參的構(gòu)造函數(shù),我們一般推薦使用的是全缺省的構(gòu)造函數(shù)(注:
無參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認構(gòu)造函數(shù),并且默認構(gòu)造函數(shù)只能有一個。無參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒寫編譯器默認生成的構(gòu)造函數(shù),三者都可以認為是默認成員函數(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)會自動生成默認的析構(gòu)函數(shù) 。
4. 對象生命周期結(jié)束時, C++ 編譯系統(tǒng)系統(tǒng)自動調(diào)用析構(gòu)函數(shù)。
這里我們用棧的例子來說明析構(gòu)函數(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ù)的實現(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ù)會置為隨機值,而自定義類型則會去調(diào)用他們的析構(gòu)函數(shù)。


三 拷貝函數(shù)
如果某些時候我們需要去復(fù)制一個對象,這樣的話我們該怎么樣去解決呢?
這里我們就需要引入拷貝函數(shù)。那么什么叫做拷貝函數(shù)呢?我們應(yīng)該去怎么實現(xiàn)呢?有什么注意事項呢?這里我們一一來說道。
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)生成默認的拷貝構(gòu)造函數(shù)。 默認的拷貝構(gòu)造函數(shù)對象按內(nèi)存存儲按字節(jié)序完成拷 貝,這種拷貝我們叫做淺拷貝,或者值拷貝。
3.拷貝函數(shù)的實現(xiàn)
拷貝函數(shù)的實現(xiàn)分為兩種一種是系統(tǒng)默認,一種是自己定義。我們分別來看其效果
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)默認生成拷貝函數(shù)(注:這里拷貝函數(shù)的拷貝對自定義類型和內(nèi)置類型的成員變量處理都是一致的完成字節(jié)序的值拷貝)


圖1 調(diào)用系統(tǒng)默認生成的拷貝函數(shù)
圖2 調(diào)用用戶自己定義的拷貝函數(shù)
在這里我們順便說一下在自定義拷貝函數(shù)的時候一定要使用引用不然會出現(xiàn)無限遞歸例如 Data(Data s){}正確的使用是Data (const Data & s){}其中const是為了保護原數(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)默認生成的拷貝函數(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)默認生成的拷貝函數(shù)是淺拷貝,那么怎么去完成深拷貝我們后邊在繼續(xù)講解。
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
深入解析C++編程中__alignof 與__uuidof運算符的使用
這篇文章主要介紹了C++編程中__alignof 與__uuidof運算符的使用,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2016-01-01
C++中的多態(tài)與虛函數(shù)的內(nèi)部實現(xiàn)方法
下面小編就為大家?guī)硪黄狢++中的多態(tài)與虛函數(shù)的內(nèi)部實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12
vscode調(diào)試gstreamer源碼的詳細流程
在本文中主要介紹了如何使用vscode調(diào)試C++和python程序,并進一步分析了如何調(diào)試gstreamer源碼,講述了如何調(diào)試gstreamer源碼的具體流程,感興趣的朋友跟隨小編一起看看吧2023-01-01
C/C++ 中堆和棧及靜態(tài)數(shù)據(jù)區(qū)詳解
這篇文章主要介紹了C/C++ 中堆和棧及靜態(tài)數(shù)據(jù)區(qū)詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04

