手機(jī)網(wǎng)站建設(shè) 豆丁關(guān)鍵詞挖掘長(zhǎng)尾詞工具
? ? ??
本文已收錄至《C++語(yǔ)言》專欄!
作者:ARMCSKGT? ? ??
目錄
前言?
正文
構(gòu)造函數(shù)
對(duì)比C和C++的初始化
構(gòu)造函數(shù)的使用與特性
默認(rèn)構(gòu)造函數(shù)
C++11關(guān)于默認(rèn)構(gòu)造缺陷的補(bǔ)丁
析構(gòu)函數(shù)
析構(gòu)函數(shù)特性
默認(rèn)析構(gòu)和自定義析構(gòu)
拷貝構(gòu)造函數(shù)
問(wèn)題聚焦?
拷貝構(gòu)造的定義和特性
使用場(chǎng)景
構(gòu)造函數(shù)小結(jié)
運(yùn)算符重載
定義方式
特性
使用說(shuō)明
運(yùn)算符重載原理
賦值運(yùn)算符重載
前后置++和--
const修飾this
取地址重載和const取地址重載
最后
前言?
C++類在設(shè)計(jì)之時(shí),規(guī)定類中有六個(gè)默認(rèn)的成員函數(shù),這些成員函數(shù)天生就存在,而且功能都很強(qiáng)大,類和對(duì)象的關(guān)鍵點(diǎn)就在這六個(gè)默認(rèn)成員函數(shù)的學(xué)習(xí),本篇將會(huì)逐一介紹這六個(gè)成員函數(shù),讓我們向類和對(duì)象的深處出發(fā)!
正文
C++規(guī)定在每個(gè)類中有六個(gè)默認(rèn)函數(shù)成員:
函數(shù) 功能 重要性 構(gòu)造函數(shù) 定義和初始化成員變量 重要 析構(gòu)函數(shù) 釋放申請(qǐng)的內(nèi)存空間(銷毀成員變量) 重要 拷貝構(gòu)造(函數(shù)) 實(shí)現(xiàn)對(duì)象間的深拷貝 重要 賦值重載(函數(shù)) 實(shí)現(xiàn)對(duì)象間的深賦值 重要 取地址重載(函數(shù)) 自定義類對(duì)象取地址操作符功能 一般 const取地址重載(函數(shù)) 自定義對(duì)象取地址操作符功能const修飾返回的地址 一般 這些函數(shù)我們不寫,編譯器也會(huì)自己寫一個(gè)默認(rèn)的函數(shù)代替對(duì)應(yīng)函數(shù)!
//以日期類的方式初見六大默認(rèn)成員函數(shù)
class Date
{
public://構(gòu)造函數(shù)Date(size_t year = 1970, size_t month = 1, size_t day = 1){_year = year;_month = month;_day = day;}//析構(gòu)函數(shù)~Date(){_year = 0;_month = 0;_day = 0;}//拷貝構(gòu)造函數(shù)(簡(jiǎn)稱:拷貝構(gòu)造)Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}//賦值重載函數(shù)(賦值運(yùn)算符重載)Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}//取地址運(yùn)算符重載Date* operator&(){return this;}//const返回取地址運(yùn)算符重載,const修飾thisconst Date* operator&() const{return this;}private:size_t _year;size_t _month;size_t _day;};
構(gòu)造函數(shù)
對(duì)比C和C++的初始化
我們?cè)谑褂肅語(yǔ)言實(shí)現(xiàn)一些例如順序表,棧等簡(jiǎn)單數(shù)據(jù)結(jié)構(gòu)時(shí),一般都會(huì)寫一個(gè)初始化函數(shù)Init,防止野指針訪問(wèn),但這樣很容易讓我們忘記去調(diào)用!
?
在C++中,為了避免這種事情,引入了構(gòu)造函數(shù),在對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用構(gòu)造函數(shù)進(jìn)行初始化,所以構(gòu)造函數(shù)是為對(duì)象自動(dòng)初始化而生的!
?
我們以日期對(duì)象為例,對(duì)比C與C++的初始化方案:
//C語(yǔ)言實(shí)現(xiàn)日期功能 typedef struct C_Date //日期數(shù)據(jù)結(jié)構(gòu)體 {size_t _year;size_t _month;size_t _day; }C_Date;void InitDate(C_Date* L)//初始化函數(shù) {L->_year = 0;L->_month = 0;L->_day = 0; }
//C++實(shí)現(xiàn)日期類 class CPP_Date //日期對(duì)象 { public://默認(rèn)構(gòu)造CPP_Date(size_t year){_year = year;_month = 1;_day = 1;}//重載實(shí)現(xiàn)多種默認(rèn)構(gòu)造方式CPP_Date(size_t year, size_t month, size_t day){_year = year;_month = month;_day = day;}private:size_t _year;size_t _month;size_t _day; };
可以發(fā)現(xiàn)C++中類融入構(gòu)造函數(shù)后只需要實(shí)例化對(duì)象就能同時(shí)完成初始化,非常方便!而且結(jié)合C++的缺少參數(shù),函數(shù)重載等新特性,可以讓初始化豐富多樣,增強(qiáng)程序可用性!
??
對(duì)比C與C++,可以發(fā)現(xiàn)C++非常貼心,就像汽車中的手動(dòng)擋與自動(dòng)擋,但兩者在不同場(chǎng)合各有千秋,在程序開發(fā)上C++的更勝一籌,在較為底層且需要更細(xì)節(jié)的程序控制時(shí)C語(yǔ)言更勝一籌!不過(guò)一般在程序開發(fā)中,C與C++可以搭配一起編程!
構(gòu)造函數(shù)的使用與特性
構(gòu)造函數(shù)是一個(gè)特殊的成員函數(shù),名字與類名相同,創(chuàng)建類類型對(duì)象時(shí)由編譯器自動(dòng)調(diào)用,以保證每個(gè)數(shù)據(jù)成員都有 一個(gè)合適的初始值,并且在對(duì)象整個(gè)生命周期內(nèi)只調(diào)用一次(實(shí)例化時(shí)被編譯器調(diào)用)。
?
構(gòu)造函數(shù)是特殊的成員函數(shù),需要注意的是,構(gòu)造函數(shù)雖然名稱叫構(gòu)造,但是構(gòu)造函數(shù)的主要任務(wù)并不是開空間創(chuàng)建對(duì)象,而是初始化對(duì)象成員變量。
?構(gòu)造函數(shù)的定義方式:
class Test { public: //構(gòu)造函數(shù)必須公開Test(參數(shù)) {} //構(gòu)造函數(shù)的函數(shù)名與類名相同且沒(méi)有返回值 };
構(gòu)造函數(shù)的特性
- 函數(shù)名與類名相同。
- 無(wú)返回值(不需要寫返回值類型,void也不需要寫)。
- 對(duì)象實(shí)例化時(shí)編譯器自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
- 構(gòu)造函數(shù)可以重載,支持多個(gè)構(gòu)造函數(shù),但是默認(rèn)的構(gòu)造函數(shù)只有一個(gè)。
使用構(gòu)造函數(shù)初始化日期對(duì)象:
//C++實(shí)現(xiàn)日期類 class CPP_Date //日期對(duì)象 { public://默認(rèn)構(gòu)造函數(shù)只允許出現(xiàn)一種顯示,如果定義全缺省就可以代替默認(rèn)構(gòu)造了//CPP_Date()//默認(rèn)構(gòu)造函數(shù)初始化-功能比較局限//{// _year = 0;// _month = 0;// _day = 0;//}//構(gòu)造函數(shù)可以重載實(shí)現(xiàn)多種構(gòu)造方式CPP_Date(size_t year){_year = year;_month = 1;_day = 1;}//一般使用全缺省值方式代替默認(rèn)構(gòu)造-智能方便CPP_Date(size_t year = 1970, size_t month = 1, size_t day = 1){_year = year;_month = month;_day = day;}private:size_t _year;size_t _month;size_t _day; };int main() {//CPP_Date d(); //注意:這種調(diào)用默認(rèn)構(gòu)造的方式是錯(cuò)誤的,調(diào)用默認(rèn)構(gòu)造不需要加()CPP_Date d1;CPP_Date d2(2022);CPP_Date d3(2023,3,12);//可以通過(guò)調(diào)用不同的構(gòu)造函數(shù)實(shí)例化多個(gè)不同的對(duì)象 }
默認(rèn)構(gòu)造函數(shù)
如果類中沒(méi)有顯式定義構(gòu)造函數(shù),則C++編譯器會(huì)自動(dòng)生成一個(gè)無(wú)參的默認(rèn)構(gòu)造函數(shù),一旦
用戶顯式定義編譯器將不再生成。那有人可能會(huì)問(wèn),既然編譯器會(huì)自動(dòng)生成構(gòu)造函數(shù),我們?yōu)槭裁催€要去寫?
注意:編譯器生成的默認(rèn)構(gòu)造函數(shù),對(duì)內(nèi)置類型不做處理,對(duì)自定義類型會(huì)調(diào)用該對(duì)象的構(gòu)造函數(shù)!
數(shù)據(jù)類型區(qū)分
- 內(nèi)置類型:int,char,double等。
- 自定義類型:struct,class等,這些自定義類型可以有自己的默認(rèn)構(gòu)造函數(shù)。
編譯器默認(rèn)構(gòu)造對(duì)內(nèi)置類型的初始化:?
編譯器默認(rèn)構(gòu)造初始化內(nèi)置類型 自定義默認(rèn)構(gòu)造:
//C++實(shí)現(xiàn)日期類 class CPP_Date //日期對(duì)象 { public://無(wú)參構(gòu)造函數(shù)//CPP_Date()//{// _year = 0;// _month = 0;// _day = 0;//}//全缺省默認(rèn)構(gòu)造CPP_Date(size_t year = 1970, size_t month = 1, size_t day = 1){_year = year;_month = month;_day = day;}//當(dāng)存在兩種實(shí)例化相同的構(gòu)造方法時(shí),只能存在一種,否則在調(diào)用時(shí)會(huì)出錯(cuò)!//例如 (CPP_Date d;) 編譯器無(wú)法判斷d對(duì)象調(diào)用的是哪一個(gè)構(gòu)造函數(shù)private:size_t _year;size_t _month;size_t _day; };
自定義默認(rèn)構(gòu)造完成內(nèi)置類型的初始化 ??
無(wú)參的構(gòu)造函數(shù)和全缺省的構(gòu)造函數(shù)都稱為默認(rèn)構(gòu)造函數(shù),并且默認(rèn)構(gòu)造函數(shù)只能有一個(gè)。
?
注意:無(wú)參構(gòu)造函數(shù)、全缺省構(gòu)造函數(shù)、我們沒(méi)寫編譯器默認(rèn)生成的構(gòu)造函數(shù),都可以認(rèn)為是默認(rèn)構(gòu)造函數(shù)。C++11關(guān)于默認(rèn)構(gòu)造缺陷的補(bǔ)丁
為了解決內(nèi)置類型無(wú)法被編譯器生成的默認(rèn)構(gòu)造初始化的問(wèn)題,在C++11中支持內(nèi)置類型在聲明階段給缺省值,當(dāng)編譯器生成默認(rèn)構(gòu)造函數(shù)時(shí),使用這些缺省值初始化內(nèi)置類型。
class CPP_Date //日期對(duì)象 { public:private:size_t _year = 1970; //聲明階段賦予缺省值size_t _month = 1;size_t _day = 1;//注意:類成員變量定義在構(gòu)造函數(shù)的初始化列表,并不是在聲明階段! };
所以,對(duì)于內(nèi)置類型要么自定義默認(rèn)構(gòu)造函數(shù),或者在聲明時(shí)賦予缺省值,對(duì)于自定義類型編譯器會(huì)調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)!
析構(gòu)函數(shù)
我們?cè)趯戫樞虮頃r(shí),在結(jié)束使用時(shí)需要調(diào)用銷毀函數(shù)是否內(nèi)存空間,但是我們可能經(jīng)常也會(huì)忘記釋放空間,析構(gòu)函數(shù)就是用來(lái)銷毀對(duì)象和釋放空間的!
??
析構(gòu)函數(shù)是特殊的成員函數(shù),其功能與構(gòu)造函數(shù)相反,析構(gòu)函數(shù)不是完成對(duì)對(duì)象本身的銷毀,局部對(duì)象銷毀工作是由編譯器完成的。而對(duì)象在銷毀時(shí)(生命周期結(jié)束時(shí))會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù),完成對(duì)象中資源的清理工作。
析構(gòu)函數(shù)特性
- 析構(gòu)函數(shù)名是在類名前加上字符 ~。
- 無(wú)參數(shù)無(wú)返回值類型(void也不需要寫)。
- 一個(gè)類只能有一個(gè)析構(gòu)函數(shù)。若未顯式定義,系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。
- 析構(gòu)函數(shù)不能重載。
- 對(duì)象生命周期結(jié)束時(shí),C++編譯系統(tǒng)系統(tǒng)自動(dòng)調(diào)用析構(gòu)函數(shù)。
? ?對(duì)于析構(gòu)函數(shù),最大的特性是在對(duì)象生命周期結(jié)束時(shí)被自動(dòng)調(diào)用,與構(gòu)造函數(shù)的區(qū)別在于不支持重載,也就是說(shuō)一個(gè)對(duì)象只能有一個(gè)析構(gòu)函數(shù)!
默認(rèn)析構(gòu)和自定義析構(gòu)
??
如果我們不寫析構(gòu)函數(shù),編譯器也會(huì)默認(rèn)生成,但編譯器生成的析構(gòu)函數(shù)對(duì)內(nèi)置類型仍然不做處理,對(duì)自定義類型會(huì)調(diào)用對(duì)應(yīng)的析構(gòu)函數(shù)處理!
?
//析構(gòu)函數(shù)定義 class Test {~Test() {//釋放方法} };
//簡(jiǎn)易棧對(duì)象 typedef int DataType; class Stack { public:Stack(size_t capacity = 3){_array = (DataType*)malloc(sizeof(DataType) * capacity);if (NULL == _array){perror("malloc申請(qǐng)空間失敗!!!");return;}_capacity = capacity;_size = 0;cout << "構(gòu)造函數(shù)初始化棧對(duì)象" << endl;}void Push(DataType data){// CheckCapacity();_array[_size] = data;_size++;}// 其他方法...~Stack(){if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}cout << "析構(gòu)函數(shù)釋放棧對(duì)象空間" << endl;} private:DataType* _array;int _capacity;int _size; };
對(duì)于自定義類型和內(nèi)置類型,是否存在析構(gòu)函數(shù)進(jìn)行釋放影響都不大,當(dāng)涉及我們自己動(dòng)態(tài)開辟空間時(shí),就需要使用析構(gòu)函數(shù)釋放空間!
注意:創(chuàng)建哪個(gè)類的對(duì)象則調(diào)用該類的析構(gòu)函數(shù),銷毀那個(gè)類的對(duì)象則調(diào)用該類的析構(gòu)函數(shù)!對(duì)于析構(gòu)函數(shù),只要是對(duì)象中涉及動(dòng)態(tài)內(nèi)存申請(qǐng),則需要使用析構(gòu)函數(shù)釋放!
拷貝構(gòu)造函數(shù)
拷貝構(gòu)造也是構(gòu)造函數(shù)的一種,但是參數(shù)不同則構(gòu)成重載,功能和特性與構(gòu)造函數(shù)不同!
問(wèn)題聚焦?
我們?cè)趧?chuàng)建對(duì)象時(shí),如果需要拷貝一個(gè)一模一樣的對(duì)象,也就是復(fù)制一個(gè)對(duì)象;那么就需要?jiǎng)?chuàng)建一個(gè)一模一樣的對(duì)象后將數(shù)據(jù)拷貝一份過(guò)去。
編譯器默認(rèn)生成的拷貝構(gòu)造函數(shù)只支持淺拷貝,也就是值拷貝,對(duì)于內(nèi)置類型,淺拷貝是沒(méi)有影響的,但是如果對(duì)象中申請(qǐng)了空間,那么該對(duì)象中必定有一個(gè)指針指向該空間的首地址,淺拷貝只會(huì)講該地址拷貝一份給另一個(gè)對(duì)象,那么會(huì)導(dǎo)致一個(gè)嚴(yán)重的問(wèn)題,就是兩個(gè)對(duì)象申請(qǐng)的空間是同一個(gè)地址,在增刪查改和析構(gòu)時(shí)拷貝的對(duì)象析構(gòu)函數(shù)會(huì)對(duì)同一片空間進(jìn)行修改和重復(fù)釋放一片空間導(dǎo)致異常,最后成為野指針問(wèn)題!
淺拷貝只是簡(jiǎn)單的逐字節(jié)拷貝,對(duì)于對(duì)象內(nèi)部有空間申請(qǐng)的,會(huì)發(fā)生共用空間重復(fù)析構(gòu)的情況;而深拷貝是在新對(duì)象中開辟一塊屬于自己的新空間,然后將數(shù)據(jù)逐一拷貝過(guò)來(lái),新舊對(duì)象之間的數(shù)據(jù)相互獨(dú)立!
?
淺拷貝下對(duì)象共用一塊空間
拷貝構(gòu)造的定義和特性
??
拷貝構(gòu)造函數(shù):只有單個(gè)形參,該形參是對(duì)本類類型對(duì)象的引用(一般常用const修飾),在用已存在的類類型對(duì)象創(chuàng)建新對(duì)象時(shí)由編譯器自動(dòng)調(diào)用。
? ?
特性
- 拷貝構(gòu)造函數(shù)是構(gòu)造函數(shù)的一個(gè)重載形式。
- 拷貝構(gòu)造函數(shù)的參數(shù)只有一個(gè)且必須是類類型對(duì)象的引用,使用傳值方式編譯器直接報(bào)錯(cuò),因?yàn)闀?huì)引發(fā)無(wú)窮遞歸調(diào)用。
?定義和調(diào)用
class Test {//拷貝構(gòu)造形參的定義方式是唯一的:(const 類型& )Test(const Test& T) {//拷貝方法} //定義方式 };int main() {Test t1;Test t2(t1); //調(diào)用拷貝構(gòu)造方式一Test t3 = t2; //調(diào)用拷貝構(gòu)造方式二return 0; }
? ?
定義須知
- 編譯器默認(rèn)生成的拷貝構(gòu)造,只是簡(jiǎn)單拷貝,只能用于非動(dòng)態(tài)內(nèi)存開辟的空間
- 拷貝構(gòu)造的參數(shù)定義方式是唯一的??
- 拷貝構(gòu)造函數(shù)函數(shù)名與構(gòu)造函數(shù)相同,不過(guò)參數(shù)類型為類對(duì)象的引用,不加引用則會(huì)發(fā)生無(wú)窮拷貝(因?yàn)樾螀⑹强截惗鴣?lái)的,這樣形參會(huì)陷入無(wú)限遞歸拷貝形參)
形參無(wú)窮遞歸拷貝 使用場(chǎng)景
//日期類示例 class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// Date(const Date& d) // 正確寫法Date(const Date& d) // 錯(cuò)誤寫法:編譯報(bào)錯(cuò),會(huì)引發(fā)無(wú)窮遞歸{_year = d._year;_month = d._month;_day = d._day;} private:int _year;int _month;int _day; };int main() {Date d1;Date d2(d1); //拷貝構(gòu)造方式一Date d3 = d2; //拷貝構(gòu)造方式二return 0; }
像日期類這樣沒(méi)有動(dòng)態(tài)內(nèi)存申請(qǐng)的對(duì)象,使用編譯器默認(rèn)生成的拷貝構(gòu)造進(jìn)行淺拷貝即可,但是向數(shù)據(jù)結(jié)構(gòu)需要動(dòng)態(tài)內(nèi)存申請(qǐng)等相對(duì)復(fù)雜的對(duì)象,就需要自定義拷貝構(gòu)造實(shí)現(xiàn)深拷貝!
? ??
拷貝構(gòu)造函數(shù)典型調(diào)用場(chǎng)景
- 使用已存在對(duì)象創(chuàng)建新對(duì)象
- 函數(shù)參數(shù)類型為類類型對(duì)象
- 函數(shù)返回值類型為類類型對(duì)象
說(shuō)明
- 對(duì)于深拷貝,是有一定代價(jià)的,為了提高程序效率,一般對(duì)象傳參時(shí),盡量使用引用類型,返回時(shí)根據(jù)實(shí)際場(chǎng)景,能用引用盡量使用引用!
- 默認(rèn)拷貝構(gòu)造函數(shù)與默認(rèn)構(gòu)造函數(shù)名相同,當(dāng)我們只寫拷貝而不寫構(gòu)造時(shí),編譯器就會(huì)報(bào)錯(cuò),因?yàn)榇藭r(shí)的拷貝會(huì)被誤以為是默認(rèn)構(gòu)造函數(shù)也就是說(shuō),默認(rèn)拷貝構(gòu)造函數(shù)存在的前提是默認(rèn)構(gòu)造函數(shù)已存在。
構(gòu)造函數(shù)小結(jié)
構(gòu)造大家族到這里基本內(nèi)容就介紹的差不多了!
類型 用途 處理情況 構(gòu)造函數(shù) 初始化對(duì)象 不對(duì)內(nèi)置類型作處理,自定義類型調(diào)用對(duì)應(yīng)構(gòu)造函數(shù) 析構(gòu)函數(shù) 銷毀對(duì)象 也不對(duì)內(nèi)置類型作處理,自定義類型調(diào)用對(duì)應(yīng)析構(gòu)函數(shù) 拷貝構(gòu)造函數(shù) 拷貝對(duì)象 只能對(duì)簡(jiǎn)單內(nèi)置類型做處理,自定義類型需要自己實(shí)現(xiàn)深拷貝
運(yùn)算符重載
C++為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。
運(yùn)算符重載的引入,主要是為了解決基本運(yùn)算符功能不足以滿足自定義類型需求的情況,例如日期類的加減,以及前后置++和--等,需要自定義對(duì)象運(yùn)算符的功能!
定義方式
返回值類型 operator操作符(參數(shù)) {//自定義操作符功能 }// operator 是運(yùn)算符重載的關(guān)鍵字
特性
不能通過(guò)連接其他符號(hào)來(lái)創(chuàng)建新的操作符:比如operator@
重載操作符必須有一個(gè)類類型參數(shù)
用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型+,不 能改變其含義
作為類成員函數(shù)重載時(shí),其形參看起來(lái)比操作數(shù)數(shù)目少一個(gè),因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的this
對(duì)于內(nèi)置運(yùn)算符,不能改變其含義
?operator操作符 就是函數(shù)名
注意這5個(gè)運(yùn)算符不能重載:(1)?.*?,(2)?:: ,(3)sizeof,(4)?:,(5)?.?
//日期類 class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//為了保證封裝性,類成員變量一般是私有,所以運(yùn)算符重載一般定義在類內(nèi)部bool operator==(const Date& d){//這里相當(dāng)于 *this 與 d 的 == 比較return _year == d._year&& _month == d._month&& _day == d._day;}private:int _year;int _month;int _day; };int main() {Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout << (d1 == d2) << endl; //這里相當(dāng)于調(diào)用 d1.operator==(d2)return 0; }
使用說(shuō)明
- operator 函數(shù)中的操作數(shù)取決于參數(shù)個(gè)數(shù)
- operator 一般定義在類中,方便訪問(wèn)類成員,當(dāng)定義在類中時(shí),運(yùn)算符左邊的對(duì)象默認(rèn)是*this
- operator 如果定義在類外,則無(wú)法訪問(wèn)類的所有成員,此時(shí)要么在類中定義特定函數(shù)獲取私有成員要么聲明為友元函數(shù),但是大部分場(chǎng)景下都沒(méi)有定義在類中更合適
運(yùn)算符重載原理
運(yùn)算符重載的原理與函數(shù)重載原理基本相同,也是對(duì)函數(shù)名修飾。
如果定義在類中在Linux環(huán)境下修飾規(guī)則為:_ZN4D+類名+運(yùn)算符英文簡(jiǎn)稱+ERKS_
賦值運(yùn)算符重載
通過(guò)上面的鋪墊,我們就要介紹下一個(gè)默認(rèn)成員,那就是賦值重載函數(shù)!
class Test {};
賦值重載是將一個(gè)對(duì)象賦值給另一個(gè)對(duì)象,與拷貝構(gòu)造相似,但是拷貝構(gòu)造是通過(guò)一個(gè)對(duì)象去實(shí)例化一個(gè)相同的對(duì)象,而賦值重載的前提是兩個(gè)對(duì)象已經(jīng)實(shí)例化存在,相互之間再賦值!本質(zhì)區(qū)別在于:一個(gè)是對(duì)象尚未實(shí)例化,另一個(gè)是兩個(gè)對(duì)象都已存在!當(dāng)兩個(gè)對(duì)象都被創(chuàng)建,并發(fā)生賦值行為時(shí),才叫做賦值重載!
??
對(duì)于這種自定義類型復(fù)制的問(wèn)題,就會(huì)涉及深拷貝和淺拷貝的問(wèn)題,所以賦值重載是很有必要的!
//日期類 class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}Date& operator=(const Date& d) //與拷貝構(gòu)造一樣能用引用就用引用{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this; //返回賦值的對(duì)象的引用(也就是自己)//這里之所以返回自己的引用是因?yàn)闀?huì)發(fā)生 d1 = d2 = d3 這樣連等的情況//使用引用可以有效避免拷貝}private:int _year;int _month;int _day; };
賦值運(yùn)算符重載格式
- 參數(shù)類型:const T&,傳遞引用可以提高傳參效率
- 返回值類型:T&,返回引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值
- 檢測(cè)是否自己給自己賦值
- 返回*this :要復(fù)合連續(xù)賦值的含義
- 賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù)
如果賦值運(yùn)算符重載成全局函數(shù),就沒(méi)有this指針了,需要給兩個(gè)參數(shù),而且此時(shí)編譯器就會(huì)報(bào)錯(cuò)error C2801: “operator =”必須是非靜態(tài)成員!
??
原因:賦值運(yùn)算符如果不顯式實(shí)現(xiàn),編譯器會(huì)生成一個(gè)默認(rèn)的。此時(shí)用戶再在類外自己實(shí)現(xiàn)一個(gè)全局的賦值運(yùn)算符重載,就和編譯器在類中生成的默認(rèn)賦值運(yùn)算符重載沖突了,故賦值運(yùn)算符重載只能是類的成員函數(shù)。
?默認(rèn)賦值重載函數(shù):用戶沒(méi)有顯式實(shí)現(xiàn)時(shí),編譯器會(huì)生成一個(gè)默認(rèn)賦值運(yùn)算符重載,以值的方式逐字節(jié)拷貝。
??
注意:內(nèi)置類型成員變量是直接賦值的,而自定義類型成員變量需要調(diào)用對(duì)應(yīng)類的賦值運(yùn)算符重載完成賦值。
??
所以賦值重載的使用環(huán)境與拷貝構(gòu)造類似:如果類中未涉及到動(dòng)態(tài)內(nèi)存申請(qǐng)(資源管理),賦值運(yùn)算符是否實(shí)現(xiàn)都可以;一旦涉及到資源管理則必須要實(shí)現(xiàn)!
//棧對(duì)象 - 猜猜是否會(huì)發(fā)生與拷貝構(gòu)造相同的問(wèn)題 typedef int DataType; class Stack { public:Stack(size_t capacity = 10){_array = (DataType*)malloc(capacity * sizeof(DataType));if (nullptr == _array){perror("malloc申請(qǐng)空間失敗");return;}_size = 0;_capacity = capacity;}void Push(const DataType& data){// CheckCapacity();_array[_size] = data;_size++;}~Stack(){if (_array){free(_array);_array = nullptr;_capacity = 0;_size = 0;}} private:DataType* _array;size_t _size;size_t _capacity; };int main() {Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2;s2 = s1;return 0; }
前后置++和--
對(duì)于自定義對(duì)象,前后置++和--還是經(jīng)常會(huì)使用到的!
class Test {Test& operator++() {} //前置++Test operator++(int) {} //后置++Test& operator--() {} //前置--Test operator--(int) {} //后置-- };
//以日期類進(jìn)行介紹 class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}// 前置++:返回+1之后的結(jié)果 // 注意:this指向的對(duì)象函數(shù)結(jié)束后不會(huì)銷毀,故以引用方式返回提高效率Date& operator++(){_day += 1;return *this;}// 后置++: // 注意:后置++是自己+1然后返回+1之前的值 // 而temp是臨時(shí)對(duì)象,因此只能以值的方式返回,不能返回引用Date operator++(int){Date temp(*this);_day += 1;return temp;} private:int _year;int _month;int _day; };int main() {Date d1;d1++;++d1;return 0; }
原理:前置++和后置++都是一元運(yùn)算符,為了讓前置++與后置++形成能正確重載,C++規(guī)定:后置++重載時(shí)多增加一個(gè)int類型的參數(shù),但調(diào)用函數(shù)時(shí)該參數(shù)不用傳遞,編譯器自動(dòng)傳遞(前后置--實(shí)現(xiàn)與++相同)。
const修飾this
將const修飾的“成員函數(shù)”稱之為const成員函數(shù),const修飾類成員函數(shù),實(shí)際修飾該成員函數(shù)隱含的this指針,表明在該成員函數(shù)中不能對(duì)類的任何成員進(jìn)行修改。
?
const常被用來(lái)修飾引用和指針,使用const修飾可以提高程序的健壯性!
使用場(chǎng)景
- 被(指針)指向?qū)ο笫浅A粱蚺R時(shí)變量
- 被引用對(duì)象是常亮或臨時(shí)變量
這些對(duì)象必須使用const修飾,避免權(quán)限放大的問(wèn)題!
修飾this指針格式
有小伙伴可能會(huì)疑惑,this指針我們不能顯示定義,那么如果要const修飾怎么辦?
對(duì)于this指針的修飾,格式為:
class Test {void Fun() const //將const加在函數(shù)參數(shù)后即可{//函數(shù)方法} };
//設(shè)想以下日期類代碼的運(yùn)行結(jié)果 class Date { public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << "Print()" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;}void Print() const{cout << "Print()const" << endl;cout << "year:" << _year << endl;cout << "month:" << _month << endl;cout << "day:" << _day << endl << endl;} private:int _year; // 年int _month; // 月int _day; // 日 };int main() {Date d1(2022, 1, 13);d1.Print();const Date d2(2022, 1, 13);d2.Print();return 0; }
代碼運(yùn)行結(jié)果 總之,const修飾this指針可以起到權(quán)限平移的作用!
取地址重載和const取地址重載
這兩個(gè)默認(rèn)成員函數(shù)一般不用重新定義 ,編譯器默認(rèn)會(huì)生成。
class Date { public:Date* operator&() //返回對(duì)象的地址{return this;}const Date* operator&() const //返回const修飾對(duì)象的地址{return this;} private:int _year; // 年int _month; // 月int _day; // 日 };
這兩個(gè)運(yùn)算符一般不需要重載,使用編譯器生成的默認(rèn)取地址的重載即可,只有特殊情況,才需要重載,比如想讓別人獲取到指定的內(nèi)容!
最后
以上就是 類和對(duì)象 - 中 的全部?jī)?nèi)容了,本篇介紹了類的六大默認(rèn)成員函數(shù),對(duì)于構(gòu)造函數(shù)何時(shí)用編譯器自動(dòng)生成的,何時(shí)自己實(shí)現(xiàn)都需要依照情況而定!對(duì)于每個(gè)成員其規(guī)則和細(xì)節(jié)都很多,需要我們?cè)陂L(zhǎng)期的使用中去牢固的掌握,掌握了這六大成員函數(shù)的使用,那么你將對(duì)類和對(duì)象的掌握又進(jìn)一步!
本次 <C++類和對(duì)象 - 中> 就介紹到這里啦,希望能夠盡可能幫助到大家。
如果文章中有瑕疵,還請(qǐng)各位大佬細(xì)心點(diǎn)評(píng)和留言,我將立即修補(bǔ)錯(cuò)誤,謝謝!
🌟其他文章閱讀推薦🌟
C++ 類和對(duì)象 - 上
C++ 入門知識(shí)
數(shù)據(jù)結(jié)構(gòu)初階?<棧>
🌹歡迎讀者多多瀏覽多多支持!🌹