C++ 类和对象(二):构造函数、析构函数、拷贝构造函数、运算符重载
- 構(gòu)造函數(shù)
- 析構(gòu)函數(shù)
- 拷貝構(gòu)造函數(shù)
- 運算符重載
class Date { };
可以看到,上面那個類沒有任何成員,是一個空類,但是它真的什么都沒有嗎?其實一個類在我們不寫的情況下,都會生成6個默認的成員函數(shù)
分別是構(gòu)造函數(shù),析構(gòu)函數(shù),拷貝構(gòu)造函數(shù),賦值運算符重載,取地址運算符重載,對const對象取地址運算符的重載
構(gòu)造函數(shù)
構(gòu)造函數(shù)是特殊的成員函數(shù),它的主要功能就是初始化對象。和我們之前c語言中自己實現(xiàn)的init函數(shù)類似。但是有一點不同的是,init是在我們創(chuàng)建完后才自己調(diào)用,而構(gòu)造函數(shù)是創(chuàng)建對象的時候由編譯器自動調(diào)用,并且在生命周期中只調(diào)用這一次。
特征:
因為構(gòu)造函數(shù)可以重載,所以我們可以根據(jù)不同的需求和場景,創(chuàng)建多個構(gòu)造函數(shù)。
同時,如果我們沒有顯式創(chuàng)建構(gòu)造函數(shù),編譯器會自動構(gòu)建一個,但如果我們已經(jīng)顯式定義了,則編譯器不會再生成。
顯示定義后則編譯器不會再默認生成。
可以使用一個有參的和一個無參的構(gòu)造函數(shù)
也可以將兩者合起來,寫一個全缺省的構(gòu)造函數(shù)
class Date { public:Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}int _year;int _month;int _day;};int main() {Date d1;Date d2(2020, 4, 19);return 0; }但是使用全缺省就不能再使用無參的,因為編譯器會無法識別此時到底該調(diào)用哪一個。
然后還有一個令人好奇的點,因為我們不實現(xiàn)構(gòu)造函數(shù)的時候編譯器會代替我們實現(xiàn),但是對于上面那個類,那個構(gòu)造函數(shù)并沒有做什么事,那些成員變量還是處于沒有初始化的狀態(tài),那默認的構(gòu)造函數(shù)有什么用呢?
這也就是默認構(gòu)造函數(shù)被稱為雙標的原因
其實默認構(gòu)造函數(shù)并不是什么都沒有做
class Date {int _year;int _month;int _day;A a1; };假設(shè)成員變量中有一個類A
當調(diào)用默認構(gòu)造函數(shù)的時候,我們會發(fā)現(xiàn)Date的默認構(gòu)造函數(shù)會去調(diào)用A的構(gòu)造函數(shù),來幫助a1初始化,所以我們可以發(fā)現(xiàn),對于內(nèi)置的類型,默認構(gòu)造函數(shù)并不會幫助它初始化,而自定義類型則會。
析構(gòu)函數(shù)
析構(gòu)函數(shù)也是一個特殊的成員函數(shù),它的功能時清理對象,和之前一些自定義類型所寫的destroy函數(shù)類似
特征:
同時,默認的析構(gòu)函數(shù)和默認的構(gòu)造函數(shù)效果一樣,會去調(diào)用自定義類型的析構(gòu)函數(shù),而不會處理內(nèi)置類型。
拷貝構(gòu)造函數(shù)
C++也為我們準備了兩種能夠通過其他對象的值來初始化一個對象的默認成員函數(shù),他們分別是拷貝構(gòu)造函數(shù)和賦值運算符重載
拷貝構(gòu)造函數(shù)
拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個重載形式。
Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}調(diào)用方法
int main() {Date d1;Date d2 = d1;Date d3(d1);//這兩種等價,都是拷貝構(gòu)造函數(shù),并且d2不是賦值運算符重載 }這是剛剛那個日期類的拷貝構(gòu)造函數(shù),即用別的對象來為本對象賦值,同時因為不需要修改引用的對象則為它加上const屬性。
注意:拷貝構(gòu)造函數(shù)的參數(shù)必須是引用,否則會引發(fā)無限遞歸調(diào)用
當我們要用d1來初始化d2的時候,需要將d1先傳遞給形參d,再用形參d進行賦值,但是d1傳遞給d的時候又會再次引發(fā)一個拷貝構(gòu)造函數(shù),這個d又會給它的拷貝構(gòu)造函數(shù)的形參d傳參,這又會引發(fā)新的,就導(dǎo)致了一個無限循環(huán),所以要加上引用
如果我們不去定義一個拷貝構(gòu)造函數(shù),編譯器也會默認創(chuàng)建一個,并且對于這個類,他們所實現(xiàn)的功能是一模一樣的。 默認的拷貝構(gòu)造函數(shù)對象按內(nèi)存存儲按字節(jié)序完成拷貝,這種拷貝我們叫做淺拷貝,或者值拷貝。 如果是上面這個日期類,當然沒問題,但如果涉及到了動態(tài)開辟的數(shù)據(jù),就會有問題了。
假設(shè)在堆上開辟了某個大小的一個數(shù)據(jù),默認的拷貝構(gòu)造函數(shù)會按照字節(jié)序來拷貝這個數(shù)據(jù),這一步其實沒問題,問題就出在析構(gòu)函數(shù)上。因為析構(gòu)函數(shù)會類的生命周期結(jié)束后將類中所有成員變量釋放,但是這種淺拷貝的數(shù)據(jù)就會存在問題,因為他們指向的是同一塊空間,而原對象和拷貝的對象會分別對它釋放一次,就導(dǎo)致了重復(fù)釋放,引發(fā)錯誤。
所以對于動態(tài)開辟的數(shù)據(jù),我們還需要使用深拷貝。
運算符重載
前幾章我寫過了函數(shù)重載,在C++中,還支持對自定義的類類型或者枚舉類型的運算符的重載。
函數(shù)原型:返回值類型 operator操作符(參數(shù)列表)
例如兩個日期類是否相等
//成員函數(shù)的操作符重載bool operator==(const Date& d2){return _year == d2._year&& _month == d2._month&& _day == d2._day;}//如果寫成普通的函數(shù)bool operator==(const Date& d1, const Date& d2) {return d1._year == d2._year&& d1._month == d2._month&& d1._day == d2._day; }int main {bool isSame = (d1 == d2)//調(diào)用時等價于 isSame = d1.operator==(d2);//或者 isSame = operator==(d1, d2); }- 不能通過連接其他符號來創(chuàng)建新的操作符:比如operator@
- 重載操作符必須有一個類類型或者枚舉類型的操作數(shù)
- 用于內(nèi)置類型的操作符,其含義不能改變,例如:內(nèi)置的整型+,不 能改變其含義
- 作為類成員的重載函數(shù)時,其形參看起來比操作數(shù)數(shù)目少1成員函數(shù)的操作符有一個默認的形參this,限定為第一個形參
- * 、:: 、sizeof 、?: 、. 注意以上5個運算符不能重載。
賦值運算符的重載
上面說過,有兩種方法能夠?qū)崿F(xiàn)用其他類來拷貝一個類,一個是拷貝構(gòu)造函數(shù),一個是賦值運算符
這里簡單的實現(xiàn)了一個,需要注意的有幾點
如果我們沒有實現(xiàn)的話,編譯器也會默認生成一個,但是這個默認的賦值重載運算符和上面的拷貝構(gòu)造函數(shù)存在著同樣的問題,它們都是淺拷貝。
默認的取地址運算符
取地址運算符也有兩個默認的成員函數(shù),編譯器默認生成,不需要我們定義,一般只有想讓別人獲取指定內(nèi)容的時候才自己定義一個。
class Date { public:Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//默認取地址重載Date* operator&(){return this;}//const取地址重載const Date* operator&()const{return this;}int _year;int _month;int _day; };總結(jié)
以上是生活随笔為你收集整理的C++ 类和对象(二):构造函数、析构函数、拷贝构造函数、运算符重载的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 类和对象(一):类的概念、类的访
- 下一篇: C++ 类和对象(三):构造函数补充、匿