日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

你好,C++(34)有一只叫做多利的羊 6.2.4 拷贝构造函数

發(fā)布時(shí)間:2023/12/19 c/c++ 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你好,C++(34)有一只叫做多利的羊 6.2.4 拷贝构造函数 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

6.2.4? 拷貝構(gòu)造函數(shù)

在C++世界中,除了需要使用構(gòu)造函數(shù)直接創(chuàng)建一個(gè)新的對(duì)象之外,有時(shí)還需要根據(jù)已經(jīng)存在的某個(gè)對(duì)象創(chuàng)建它的一個(gè)副本,就像那只叫做多利的羊一樣,我們希望根據(jù)一只羊創(chuàng)建出來另外一只一模一樣的羊。例如:

// 調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新對(duì)象shMother Sheep shMother; // 對(duì)shMother進(jìn)行一些操作… // 利用shMother對(duì)象創(chuàng)建一個(gè)一模一樣的新對(duì)象shDolly作為其副本 Sheep shDolly(shMother);

在這里,首先創(chuàng)建了一個(gè)Sheep類的新對(duì)象shMother,然后對(duì)它進(jìn)行了一些操作改變其成員變量等,接著用這個(gè)對(duì)象作為Sheep類的構(gòu)造函數(shù)的參數(shù),創(chuàng)建一個(gè)與shMother對(duì)象一模一樣的副本shDolly。我們將這種可以接受某個(gè)對(duì)象作為參數(shù)并創(chuàng)建一個(gè)新對(duì)象作為其副本的構(gòu)造函數(shù)稱為拷貝構(gòu)造函數(shù)。拷貝構(gòu)造函數(shù)實(shí)際上是構(gòu)造函數(shù)的表親,在語法格式上,兩者基本相似,兩者擁有相同的函數(shù)名,只是拷貝構(gòu)造函數(shù)的參數(shù)是這個(gè)類對(duì)象的引用,而它所創(chuàng)建得到的對(duì)象,是對(duì)那個(gè)作為參數(shù)的對(duì)象的一個(gè)拷貝,是它的一個(gè)副本。跟構(gòu)造函數(shù)相似,默認(rèn)情況下,如果一個(gè)類沒有顯式地定義其拷貝構(gòu)造函數(shù),編譯器會(huì)為其創(chuàng)建一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),以內(nèi)存拷貝的方式將舊有對(duì)象內(nèi)存空間中的數(shù)據(jù)拷貝到新對(duì)象的內(nèi)存空間,以此來完成新對(duì)象的創(chuàng)建。因?yàn)槲覀兩厦娴腟heep類沒有定義拷貝構(gòu)造函數(shù),上面代碼中shDolly這個(gè)對(duì)象的創(chuàng)建就是通過這種默認(rèn)的拷貝構(gòu)造函數(shù)的方式完成的。

使用default和delete關(guān)鍵字控制類的默認(rèn)行為

為了提高開發(fā)效率,對(duì)于類中必需的某些特殊函數(shù),比如構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值操作符等,如果我們沒有在類當(dāng)中顯式地定義這些特殊函數(shù),編譯器就會(huì)為我們生成這些函數(shù)的默認(rèn)版本。雖然這種機(jī)制可以為我們節(jié)省很多編寫特殊函數(shù)的時(shí)間,但是在某些特殊情況下,比如我們不希望對(duì)象被復(fù)制,就不需要編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)。這時(shí)候這種機(jī)制反倒成了畫蛇添足,多此一舉了。

為了取消編譯器的這種默認(rèn)行為,我們可以使用delete關(guān)鍵字來禁用某一個(gè)默認(rèn)的特殊函數(shù)。比如,在默認(rèn)情況下,即使我們沒在類當(dāng)中定義拷貝構(gòu)造函數(shù),編譯器也會(huì)為我們生成默認(rèn)的拷貝構(gòu)造函數(shù)進(jìn)而可以通過它完成對(duì)象的拷貝復(fù)制。而有的時(shí)候,我們不希望某個(gè)對(duì)象被復(fù)制,那就需要用delete禁用類當(dāng)中的拷貝構(gòu)造函數(shù)和賦值操作符,防止編譯器為其生成默認(rèn)函數(shù):

class Sheep {// ...// 禁用類的默認(rèn)賦值操作符Sheep& operator = (const Sheep&) = delete; // 禁用類的默認(rèn)拷貝構(gòu)造函數(shù)Sheep(const Sheep&) = delete; };

現(xiàn)在,Sheep類就沒有默認(rèn)的賦值操作符和拷貝構(gòu)造函數(shù)的實(shí)現(xiàn)了。如果這時(shí)還想對(duì)對(duì)象進(jìn)行復(fù)制,就會(huì)導(dǎo)致編譯錯(cuò)誤,從而達(dá)到禁止對(duì)象被復(fù)制的目的。例如:

// 錯(cuò)誤的對(duì)象復(fù)制行為 Sheep shDolly(shMother); // 錯(cuò)誤:Sheep類的拷貝構(gòu)造函數(shù)被禁用 Sheep shDolly = shMother; // 錯(cuò)誤:Sheep類的賦值操作符被禁用

與delete關(guān)鍵字禁用默認(rèn)函數(shù)相反地,我們也可以使用default關(guān)鍵字,顯式地表明我們希望使用編譯器為這些特殊函數(shù)生成的默認(rèn)版本。還是上面的例子,如果我們希望對(duì)象可以以默認(rèn)方式被復(fù)制:

class Sheep {// ...// 使用默認(rèn)的賦值操作符和拷貝構(gòu)造函數(shù)Sheep& operator = (const Sheep&) = default; Sheep(const Sheep&) = default; };

顯式地使用default關(guān)鍵字來表明使用類的默認(rèn)行為,對(duì)于編譯器來說顯然是多余的,因?yàn)榧词刮覀儾徽f明,它也會(huì)那么干。但是對(duì)于代碼的閱讀者而言,使用default關(guān)鍵字顯式地表明使用特殊函數(shù)的默認(rèn)版本,則意味著我們已經(jīng)考慮過,這些特殊函數(shù)的默認(rèn)版本已經(jīng)滿足我們的要求,無需自己另外定義。而將默認(rèn)的操作留給編譯器去實(shí)現(xiàn),不僅可以節(jié)省時(shí)間提高效率,更重要的是,減少錯(cuò)誤發(fā)生的機(jī)會(huì),并且通常會(huì)產(chǎn)生更好的目標(biāo)代碼。

在大多數(shù)情況下,默認(rèn)版本的拷貝構(gòu)造函數(shù)已經(jīng)能夠滿足我們拷貝復(fù)制對(duì)象的需要了,我們無需顯式地定義拷貝構(gòu)造函數(shù)。但在某些特殊情況下,特別是類當(dāng)中有指針類型的成員變量的時(shí)候,以拷貝內(nèi)存方式實(shí)現(xiàn)的默認(rèn)拷貝構(gòu)造函數(shù)只能復(fù)制指針成員變量的值,而不能復(fù)制指針?biāo)赶虻膬?nèi)容,這樣,新舊兩個(gè)對(duì)象中不同的兩個(gè)指針卻指向了相同的內(nèi)容,這顯然是不合理的。默認(rèn)的拷貝構(gòu)造函數(shù)無法正確地完成這類對(duì)象的拷貝。在這種情況下,就需要自己定義類的拷貝構(gòu)造函數(shù),以自定義的方式完成像指針成員變量這樣的需要特殊處理的內(nèi)容的拷貝工作。例如,有一個(gè)Computer類,它有一個(gè)指針類型的成員變量m_pKeyboard,指向的是一個(gè)獨(dú)立的Keboard對(duì)象,這時(shí)就需要定義Compuer類的拷貝構(gòu)造函數(shù)來完成特殊的復(fù)制工作:

// 鍵盤類,因?yàn)榻Y(jié)構(gòu)簡單,我們使用struct來定義 struct Keyboard {// 鍵盤的型string m_strModel; };// 定義了拷貝構(gòu)造函數(shù)的電腦類 class Computer { public:// 默認(rèn)構(gòu)造函數(shù) Computer(): m_pKeyboard(nullptr),m_strModel(""){}// 拷貝構(gòu)造函數(shù),參數(shù)是const修飾的Computer類的引用Computer(const Computer& com): m_strModel(com.m_strModel) // 直接使用初始化屬性列表完成對(duì)象類型成員變量m_strModel的復(fù)制 {// 創(chuàng)建新對(duì)象,完成指針類型成員變量m_pKeyboard的復(fù)制// 獲得已有對(duì)象com的指針類型成員變量m_pKeyboardKeyboard* pOldKeyboard = com.GetKeyboard();// 以pOldKeyboard所指向的Keyboard對(duì)象為藍(lán)本,// 創(chuàng)建一個(gè)新的Keyboard對(duì)象,并讓m_Keyboard指向這個(gè)對(duì)象if( nullptr != pOldKeyboard )// 這里Keyboard對(duì)象的復(fù)制使用的是Keyboard類的默認(rèn)拷貝構(gòu)造函數(shù)m_pKeyboard = new Keyboard(*(pOldKeyboard));elsem_pKeyboard = nullptr; // 如果沒有鍵盤 }// 析構(gòu)函數(shù), // 對(duì)于對(duì)象類型的成員變量m_strModel,會(huì)被自動(dòng)銷毀,無需在析構(gòu)函數(shù)中進(jìn)行處理 // 對(duì)于指針類型的成員變量m_pKeyboard,則需要在析構(gòu)函數(shù)中主動(dòng)銷毀 ~Computer() {delete m_pKeyboard;m_pKeyboard = nullptr; }// 成員函數(shù),設(shè)置或獲得鍵盤對(duì)象指針void SetKeyboard(Keyboard* pKeyboard){m_pKeyboard = pKeyboard;}Keyboard* GetKeyboard() const{return m_pKeyboard;}private:// 指針類型的成員變量Keyboard* m_pKeyboard;// 對(duì)象類型的成員變量string m_strModel; };

在這段代碼中,我們?yōu)镃omputer類創(chuàng)建了一個(gè)自定義的拷貝構(gòu)造函數(shù)。在這個(gè)拷貝構(gòu)造函數(shù)中,對(duì)于對(duì)象類型的成員變量m_strModel,我們直接使用初始化屬性列表就完成了成員變量的拷貝。而對(duì)于指針類型成員變量m_pKeyboard而言,它的拷貝并不是拷貝這個(gè)指針的值本身,而應(yīng)該拷貝的是這個(gè)指針?biāo)赶虻膶?duì)象。所以,對(duì)于指針類型的成員變量,并不能直接采用內(nèi)存拷貝的形式完成拷貝,那樣只是拷貝了指針的值,而指針?biāo)赶虻膬?nèi)容并沒有得到拷貝。要完成指針類型成員變量的拷貝,首先應(yīng)該獲得已有對(duì)象的指針類型成員變量,進(jìn)而通過它獲得它所指向的對(duì)象,然后再創(chuàng)建一個(gè)副本并將新對(duì)象中相應(yīng)的指針類型成員變量指向這個(gè)對(duì)象,比如我們這里的“m_pKeyboard = new Keyboard(*(pOldKeyboard));”,這樣才完成了指針類型成員變量的復(fù)制。這個(gè)我們自己定義的拷貝構(gòu)造函數(shù)不僅能夠拷貝Computer類的對(duì)象類型成員變量m_strModel,也能夠正確地完成指針類型成員變量m_pKeyboard的拷貝,最終才能完成對(duì)Computer對(duì)象的拷貝。例如:

// 引入斷言所在的頭文件 #include <assert.h>//// 創(chuàng)建一個(gè)Computer對(duì)象oldcom Computer oldcom;// 創(chuàng)建oldcom的Keyboard對(duì)象并修改其屬性 Keyboard keyboard;keyboard.m_strModel = "Microsoft-101";// 將鍵盤組裝到oldcom上 oldcom.SetKeyboard(&keyboard);// 以oldcom為藍(lán)本,利用Computer類的拷貝構(gòu)造函數(shù)創(chuàng)建新對(duì)象newcom// 新的newcom對(duì)象是oldcom對(duì)象的一個(gè)副本 Computer newcom(oldcom);// 使用斷言判斷兩個(gè)Computer對(duì)象是否相同, // 電腦型號(hào)應(yīng)該相同 assert(newcom.GetModel() == oldcom.GetModel()); // 不同的Computer對(duì)象應(yīng)該擁有不同的Keyboard對(duì)象 assert( newcom.GetKeyboard() != oldcom.GetKeyboard() ); // 因?yàn)槭菑?fù)制,不同的Keyboard對(duì)象應(yīng)該是相同的型號(hào) assert( newcom.GetKeyboard()->m_strModel== oldcom.GetKeyboard()->m_strModel );

在C++中,除了使用拷貝構(gòu)造函數(shù)創(chuàng)建對(duì)象的副本作為新的對(duì)象之外,在創(chuàng)建一個(gè)新對(duì)象之后,還常常將一個(gè)已有的對(duì)象直接賦值給它來完成新對(duì)象的初始化。例如:

// 創(chuàng)建一個(gè)新的對(duì)象 Computer newcom; // 利用一個(gè)已有的對(duì)象對(duì)其進(jìn)行賦值,完成初始化 newcom = oldcom;

賦值的過程,實(shí)際上也是一個(gè)拷貝的過程,就是將等號(hào)右邊的對(duì)象拷貝到等號(hào)左邊的對(duì)象。跟類的拷貝構(gòu)造函數(shù)相似,如果沒有顯式地為類定義賦值操作符,編譯器也會(huì)為其生成一個(gè)默認(rèn)的賦值操作符,以內(nèi)存拷貝的方式完成對(duì)象的賦值操作。因?yàn)橥瑯邮且詢?nèi)存拷貝的方式完成對(duì)象的復(fù)制,所以當(dāng)類中有指針型成員變量時(shí),也同樣會(huì)遇到只能拷貝指針的值而無法拷貝指針?biāo)赶虻膬?nèi)容的問題。因此,要完成帶有指針型成員變量的類對(duì)象的賦值,必須對(duì)類的賦值操作符進(jìn)行自定義,在其中以自定義的方式來完成指針型成員變量的復(fù)制。例如,Computer類中含有指針型成員變量m_pKeybard,可以這樣自定義它的賦值操作符來完成其賦值操作。

// 定義了賦值操作符“=”的電腦類 class Computer { public:// 自定義的賦值操作符Computer& operator = (const Computer& com){// 判斷是否是自己給自己賦值// 如果是自賦值,則直接返回對(duì)象本身 // 這里的this指針,是類當(dāng)中隱含的一個(gè)指向自身對(duì)象的指針。if( this == &com ) return *this;// 直接完成對(duì)象型成員變量的賦值m_strModel = com.m_strModel;// 創(chuàng)建舊有對(duì)象的指針型成員變量所指對(duì)象的副本// 并將被賦值對(duì)象相應(yīng)的指針型成員變量指向這個(gè)副本對(duì)象m_pKeyboard = new Keyboard(*(com.GetKeyboard()));}// };

在上面的賦值操作符函數(shù)中,我們首先判斷這是不是一個(gè)自賦值操作。所謂自賦值,就是自己給自己賦值。例如:

// 用newcom給newcom賦值 newcom = newcom;

嚴(yán)格意義上說,這種自賦值操作是沒有意義的,應(yīng)該算是程序員的一個(gè)失誤。但作為一個(gè)設(shè)計(jì)良好的賦值操作符,應(yīng)該可以檢測出這種失誤并給予恰當(dāng)?shù)奶幚?#xff0c;將程序從程序員的失誤中解救出來:

// 判斷是否是自賦值操作 // 將this指針與傳遞進(jìn)來的指向com對(duì)象的指針進(jìn)行比較 // 如果相等,就是自賦值操作,直接返回這個(gè)對(duì)象本身 if( this == &com) return *this;

在賦值操作符函數(shù)中如果檢測到這種自賦值操作,它就直接返回這個(gè)對(duì)象本身從而避免后面的復(fù)制操作。如果不是自賦值操作,對(duì)于對(duì)象型成員變量,使用“=”操作符直接完成其賦值;而對(duì)于指針型成員變量,則采用跟拷貝構(gòu)造函數(shù)相似的方式,通過創(chuàng)建它所指向的對(duì)象的副本,并將左側(cè)對(duì)象的相應(yīng)指針型成員變量指向這個(gè)副本對(duì)象來完成其賦值。

另外值得注意的一點(diǎn)是,賦值操作符的返回值類型并不一定是這個(gè)類的引用,我們使用void代替也是可以的。例如:

class Computer { public:// 以void作為返回值類型的賦值操作符void operator = (const Computer& com){// }// };

以上的代碼雖然在語法上正確,也能夠?qū)崿F(xiàn)單個(gè)對(duì)象的賦值操作,但是卻無法實(shí)現(xiàn)如下形式的連續(xù)賦值操作:

Computer oldcom; // Computer newcom1,newcom2; // 連續(xù)賦值 newcom1 = newcom2 = oldcom;

連續(xù)的賦值操作符是從右向左開始進(jìn)行賦值的,所以,上面的代碼實(shí)際上就是:

newcom1 = (newcom2 = oldcom );

也就是先將oldcom賦值給newcom2(如果返回值類型是void,這一步是可以完成的),然后將“newcom2 = oldcom”的運(yùn)算結(jié)果賦值給newcom1,而如果賦值操作符的返回值類型是void,也就意味著“newcom2 = oldcom”的運(yùn)算結(jié)果是void類型,我們顯然是不能將一個(gè)void類型數(shù)據(jù)賦值給一個(gè)Computer對(duì)象的。所以,為了實(shí)現(xiàn)上面這種形式的連續(xù)賦值,我們通常以這個(gè)類的引用(Computer&)作為賦值操作符的返回值類型,并在其中返回這個(gè)對(duì)象本身(“return *this”),已備用做下一步的繼續(xù)賦值操作。

初始化列表構(gòu)造函數(shù)

除了我們?cè)谏衔闹薪榻B的普通構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)之外,為了讓對(duì)象的創(chuàng)建形式更加靈活,C++還提供了一種可以接受一個(gè)初始化列表(initializer list)為參數(shù)的構(gòu)造函數(shù),因此這種構(gòu)造函數(shù)也被稱為初始化列表構(gòu)造函數(shù)。初始化列表由一對(duì)大括號(hào)(“{}”)構(gòu)造而成,可以包含任意多個(gè)相同類型的數(shù)據(jù)元素。如果我們希望可以通過不定個(gè)數(shù)的相同類型數(shù)據(jù)來創(chuàng)建某個(gè)對(duì)象,比如,一個(gè)工資對(duì)象管理著不定個(gè)數(shù)的工資項(xiàng)目,包括基本工資,獎(jiǎng)金,提成,補(bǔ)貼等等,有的人只有基本工資,而有的人全都有,為了創(chuàng)建工資對(duì)象形式上的統(tǒng)一,我們就希望這些不定個(gè)數(shù)的工資項(xiàng)目都可以用來創(chuàng)建工資對(duì)象。這時(shí),我們就需要通過實(shí)現(xiàn)工資類的初始化列表構(gòu)造函數(shù)來完成這一任務(wù):

#include <iostream> #include <vector> #include <initializer_list> // 引入初始化列表所在頭文件using namespace std;// 工資類 class Salary { public: // 初始化列表構(gòu)造函數(shù) // 工資數(shù)據(jù)為int類型,所以其參數(shù)類型為initializer_list<int>Salary(initializer_list<int> s){// 以容器的形式訪問初始化列表// 獲取其中的工資項(xiàng)目保存到工資類的vector容器for(int i : s)m_vecSalary.push_back(i); }// ..// 獲取工資總數(shù) int GetTotal() {int nTotal = 0;for(int i : m_vecSalary)nTotal += i;return nTotal; }private:// 保存工資數(shù)據(jù)的vector容器vector<int> m_vecSalary; };int main() { // 陳老師只有基本工資,“{}”表示初始化列表Salary sChen{2200};// 王老師既有基本工資還有獎(jiǎng)金和補(bǔ)貼 Salary sWang{5000,9500,1003};// 輸出結(jié)果 cout<<"陳老師的工資:"<<sChen.GetTotal()<<endl;cout<<"王老師的工資:"<<sWang.GetTotal()<<endl;return 0; }

從這里可以看到,雖然陳老師和王老師的工資項(xiàng)目各不相同,但是通過初始化列表構(gòu)造函數(shù),他們的工資對(duì)象都可以以統(tǒng)一的形式創(chuàng)建。而這正是初始化列表的意義所在,它可以讓不同個(gè)數(shù)的同類型數(shù)據(jù)以相同的形式作為函數(shù)參數(shù)。換句話說,如果我們希望某個(gè)函數(shù)可以接受不定個(gè)數(shù)的同類型數(shù)據(jù)為參數(shù),就可以用初始化列表作為參數(shù)類型。例如,我們可以為Salary類添加一個(gè)AddSalary()函數(shù),用初始化列表作為參數(shù),它就可以向Salary對(duì)象添加不定個(gè)數(shù)的工資項(xiàng)目:

// 以初始化列表為參數(shù)的普通函數(shù) void AddSalary(initializer_list<int> s) {for(int i : s)m_vecSalary.push_back(i); }//// 后來發(fā)現(xiàn)是陳老師的獎(jiǎng)金和補(bǔ)貼忘了計(jì)算了,給他加上 // 這里的大括號(hào){}就構(gòu)成初始化列表 sChen.AddSalary({8200,6500});

?

6.2.5? 操作符重載

如果要想對(duì)兩個(gè)對(duì)象進(jìn)行操作,比如兩個(gè)對(duì)象相加,最直觀的方式就是像數(shù)學(xué)式子一樣,用表示相應(yīng)意義的操作符來連接兩個(gè)對(duì)象,以此表達(dá)對(duì)這兩個(gè)對(duì)象的操作。在本質(zhì)上,操作符就相當(dāng)于一個(gè)函數(shù),它有自己的參數(shù),可以用來接收操作符所操作的數(shù)據(jù);也有自己的函數(shù)名,就是操作符號(hào);同時(shí)也有返回值,用于返回結(jié)果數(shù)據(jù)。而在使用上,只需要用操作符連接被操作的兩個(gè)對(duì)象,比函數(shù)調(diào)用簡單直觀得多,代碼的可讀性更好。所以在表達(dá)一些常見的操作時(shí),比如對(duì)兩個(gè)對(duì)象的加減操作,我們往往通過重載這個(gè)類的相應(yīng)意義的操作符來完成。在C++中有許多內(nèi)置的數(shù)據(jù)類型,包括int、char、string等,而這些內(nèi)置的數(shù)據(jù)類型都有許多已經(jīng)定義的操作符可以用來表達(dá)它們之間的操作。比如,我們可以用“+”操作符來表達(dá)兩個(gè)對(duì)象之間的“加和”操作,用它連接兩個(gè)int對(duì)象,得到的“加和”操作結(jié)果就是這兩個(gè)數(shù)的和,而用它連接兩個(gè)string對(duì)象,得到的“加和”操作結(jié)果就是將兩個(gè)字符串連接到一起。例如:

int a = 3; int b = 4; // 使用加法操作符“+”獲得兩個(gè)int類型變量的和 int c = a + b; cout<<a<<" + "<<b<< " = "<<c<<endl;string strSub1("Hello "); string strSub2("C++"); // 使用加法操作符“+”獲得兩個(gè)string類型變量的連接結(jié)果 string strCombin = strSub1 + strSub2; cout<<strSub1<<"+ "<<strSub2<<" = "<<strCombin<<endl;

這種用操作符來表達(dá)對(duì)象之間的操作關(guān)系的方式,用抽象性的操作符表達(dá)了具體的操作過程,從而隱藏了操作過程的具體細(xì)節(jié),既直觀又自然,也就更便于使用。對(duì)于內(nèi)置的數(shù)據(jù)類型,C++已經(jīng)提供了豐富的操作符供我們選擇使用以完成常見的操作,比如表示數(shù)學(xué)運(yùn)算的“+”(加)、“-”(減)、“*”(乘)、“/”(除)。但是對(duì)于我們新定義的類而言,其兩個(gè)對(duì)象之間是不能用這些操作符直接進(jìn)行操作的。比如,分別定義了Father類和Mother類的兩個(gè)對(duì)象,我們希望可以用加法操作符“+”連接這兩個(gè)對(duì)象,進(jìn)而通過運(yùn)算得出一個(gè)Baby類的對(duì)象:

// 分別定義Father類和Mother類的對(duì)象 Father father; Mother mother;// 用加法操作符“+”連接兩個(gè)對(duì)象,運(yùn)算得到Baby類的對(duì)象 Baby baby = father + mother;

以上語句所表達(dá)的是一件顯而易見的事情,但是,如果沒有對(duì)Father類的加法操作符“+”進(jìn)行定義,Father類是不知道如何和一個(gè)Mother類的對(duì)象加起來創(chuàng)建一個(gè)Baby類對(duì)象的,這樣的語句會(huì)出現(xiàn)編譯錯(cuò)誤,一件顯而易見的事情在C++中卻行不通。但幸運(yùn)的是,C++允許我們對(duì)這些操作符進(jìn)行重載,讓我們可以對(duì)操作符的行為進(jìn)行自定義。既然是自定義,自然是想干啥就干啥,自然也就可以讓Father類對(duì)象加上Mother類對(duì)象得到Baby類對(duì)象,讓上面的代碼成為可能。

在功能上,重載操作符等同于類的成員函數(shù),兩者并無本質(zhì)上的差別,可以簡單地將重載操作符看成是一類比較特殊的成員函數(shù)。雖然成員函數(shù)可以提供跟操作符相同的功能,但是運(yùn)用操作符可以讓語句更加自然簡潔,也更具可讀性。比如,“a.add(b)”調(diào)用函數(shù)add()以實(shí)現(xiàn)兩個(gè)對(duì)象a和b相加,但是表達(dá)相同意義的“a + b”語句,遠(yuǎn)比“a.add(b)”更直觀也更容易讓人理解。

在C++中,聲明重載操作符的語法格式如下:

class 類名 { public:返回值類型 operator 操作符 (參數(shù)列表){// 操作符的具體運(yùn)算過程 } };

從這里可以看到,重載操作符和類的成員函數(shù)在本質(zhì)上雖然相同,但在形式上還是存在一些細(xì)微的差別。普通成員函數(shù)以標(biāo)識(shí)符(不以數(shù)字為首的字符串)作為函數(shù)名,而重載操作符以“operator 操作符”作為函數(shù)名。其中的“operator”表示這是一個(gè)重載的操作符函數(shù),而其后的操作符就是我們要定義的符號(hào)。

在使用上,當(dāng)使用操作符連接兩個(gè)對(duì)象進(jìn)行運(yùn)算時(shí),實(shí)際上相當(dāng)于調(diào)用第一個(gè)對(duì)象的操作符函數(shù),而第二個(gè)對(duì)象則作為這個(gè)操作符函數(shù)的參數(shù)。例如,使用加法操作符對(duì)兩個(gè)對(duì)象進(jìn)行運(yùn)算:

a + b;

這條語句實(shí)際上等同于:

a.operator + (b);

“a + b”表示調(diào)用的是對(duì)象a的操作符“operator +”,而對(duì)象b則是這個(gè)操作符函數(shù)的參數(shù)。理解了這些,要想讓“father + mother”得到baby對(duì)象,只需要定義Father類的“+”操作符函數(shù)(因?yàn)閒ather位于操作符之前,所以我們定義father所屬的Father類的操作符),使其可以接受一個(gè)Mother類的對(duì)象作為參數(shù),并返回一個(gè)Baby類的對(duì)象就可以了:

// 母親類class Mother{// 省略具體定義 };// 孩子類class Baby{public:// 孩子類的構(gòu)造函數(shù) Baby(string strName): m_strName(strName){}private:// 孩子的名字string m_strName;};// 父親類class Father { public:// 重載操作符“+”,返回值為Baby類型,參數(shù)為Mother類型Baby operator + (const Mother& mom){ // 創(chuàng)建一個(gè)Baby對(duì)象并返回,省略創(chuàng)建過程…return Baby("MiaoMiao");} };

在Father類的重載操作符“+”中,它可以接受一個(gè)Mother類的對(duì)象作為參數(shù),并在其中創(chuàng)建一個(gè)Baby類的對(duì)象作為操作符的返回值。這樣就完整地表達(dá)了一個(gè)Father類的對(duì)象加上一個(gè)Mother類的對(duì)象得到一個(gè)Baby類的對(duì)象的意義,現(xiàn)在就可以方便地使用操作符“+”將Father類的對(duì)象和Mother類的對(duì)象相加而得到一個(gè)Baby類的對(duì)象了。需要注意的是,這里我們只是定義了Father類的“+”操作符,所以在用它計(jì)算的時(shí)候,只能是Father類的對(duì)象放在“+”之前,而如果希望Mother類的對(duì)象也可以放在“+”之前,相應(yīng)地就同樣需要定義Mother類的“+”操作符。

轉(zhuǎn)載于:https://www.cnblogs.com/nihaoCPP/p/operator_overload.html

總結(jié)

以上是生活随笔為你收集整理的你好,C++(34)有一只叫做多利的羊 6.2.4 拷贝构造函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。