你好,C++(34)有一只叫做多利的羊 6.2.4 拷贝构造函数
6.2.4? 拷貝構(gòu)造函數(shù)
在C++世界中,除了需要使用構(gòu)造函數(shù)直接創(chuàng)建一個新的對象之外,有時還需要根據(jù)已經(jīng)存在的某個對象創(chuàng)建它的一個副本,就像那只叫做多利的羊一樣,我們希望根據(jù)一只羊創(chuàng)建出來另外一只一模一樣的羊。例如:
// 調(diào)用構(gòu)造函數(shù)創(chuàng)建一個新對象shMother Sheep shMother; // 對shMother進行一些操作… // 利用shMother對象創(chuàng)建一個一模一樣的新對象shDolly作為其副本 Sheep shDolly(shMother);在這里,首先創(chuàng)建了一個Sheep類的新對象shMother,然后對它進行了一些操作改變其成員變量等,接著用這個對象作為Sheep類的構(gòu)造函數(shù)的參數(shù),創(chuàng)建一個與shMother對象一模一樣的副本shDolly。我們將這種可以接受某個對象作為參數(shù)并創(chuàng)建一個新對象作為其副本的構(gòu)造函數(shù)稱為拷貝構(gòu)造函數(shù)。拷貝構(gòu)造函數(shù)實際上是構(gòu)造函數(shù)的表親,在語法格式上,兩者基本相似,兩者擁有相同的函數(shù)名,只是拷貝構(gòu)造函數(shù)的參數(shù)是這個類對象的引用,而它所創(chuàng)建得到的對象,是對那個作為參數(shù)的對象的一個拷貝,是它的一個副本。跟構(gòu)造函數(shù)相似,默認(rèn)情況下,如果一個類沒有顯式地定義其拷貝構(gòu)造函數(shù),編譯器會為其創(chuàng)建一個默認(rèn)的拷貝構(gòu)造函數(shù),以內(nèi)存拷貝的方式將舊有對象內(nèi)存空間中的數(shù)據(jù)拷貝到新對象的內(nèi)存空間,以此來完成新對象的創(chuàng)建。因為我們上面的Sheep類沒有定義拷貝構(gòu)造函數(shù),上面代碼中shDolly這個對象的創(chuàng)建就是通過這種默認(rèn)的拷貝構(gòu)造函數(shù)的方式完成的。
使用default和delete關(guān)鍵字控制類的默認(rèn)行為
為了提高開發(fā)效率,對于類中必需的某些特殊函數(shù),比如構(gòu)造函數(shù)、析構(gòu)函數(shù)、賦值操作符等,如果我們沒有在類當(dāng)中顯式地定義這些特殊函數(shù),編譯器就會為我們生成這些函數(shù)的默認(rèn)版本。雖然這種機制可以為我們節(jié)省很多編寫特殊函數(shù)的時間,但是在某些特殊情況下,比如我們不希望對象被復(fù)制,就不需要編譯器生成的默認(rèn)拷貝構(gòu)造函數(shù)。這時候這種機制反倒成了畫蛇添足,多此一舉了。
為了取消編譯器的這種默認(rèn)行為,我們可以使用delete關(guān)鍵字來禁用某一個默認(rèn)的特殊函數(shù)。比如,在默認(rèn)情況下,即使我們沒在類當(dāng)中定義拷貝構(gòu)造函數(shù),編譯器也會為我們生成默認(rèn)的拷貝構(gòu)造函數(shù)進而可以通過它完成對象的拷貝復(fù)制。而有的時候,我們不希望某個對象被復(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ù)的實現(xiàn)了。如果這時還想對對象進行復(fù)制,就會導(dǎo)致編譯錯誤,從而達到禁止對象被復(fù)制的目的。例如:
// 錯誤的對象復(fù)制行為 Sheep shDolly(shMother); // 錯誤:Sheep類的拷貝構(gòu)造函數(shù)被禁用 Sheep shDolly = shMother; // 錯誤:Sheep類的賦值操作符被禁用與delete關(guān)鍵字禁用默認(rèn)函數(shù)相反地,我們也可以使用default關(guān)鍵字,顯式地表明我們希望使用編譯器為這些特殊函數(shù)生成的默認(rèn)版本。還是上面的例子,如果我們希望對象可以以默認(rèn)方式被復(fù)制:
class Sheep {// ...// 使用默認(rèn)的賦值操作符和拷貝構(gòu)造函數(shù)Sheep& operator = (const Sheep&) = default; Sheep(const Sheep&) = default; };顯式地使用default關(guān)鍵字來表明使用類的默認(rèn)行為,對于編譯器來說顯然是多余的,因為即使我們不說明,它也會那么干。但是對于代碼的閱讀者而言,使用default關(guān)鍵字顯式地表明使用特殊函數(shù)的默認(rèn)版本,則意味著我們已經(jīng)考慮過,這些特殊函數(shù)的默認(rèn)版本已經(jīng)滿足我們的要求,無需自己另外定義。而將默認(rèn)的操作留給編譯器去實現(xiàn),不僅可以節(jié)省時間提高效率,更重要的是,減少錯誤發(fā)生的機會,并且通常會產(chǎn)生更好的目標(biāo)代碼。
在大多數(shù)情況下,默認(rèn)版本的拷貝構(gòu)造函數(shù)已經(jīng)能夠滿足我們拷貝復(fù)制對象的需要了,我們無需顯式地定義拷貝構(gòu)造函數(shù)。但在某些特殊情況下,特別是類當(dāng)中有指針類型的成員變量的時候,以拷貝內(nèi)存方式實現(xiàn)的默認(rèn)拷貝構(gòu)造函數(shù)只能復(fù)制指針成員變量的值,而不能復(fù)制指針?biāo)赶虻膬?nèi)容,這樣,新舊兩個對象中不同的兩個指針卻指向了相同的內(nèi)容,這顯然是不合理的。默認(rèn)的拷貝構(gòu)造函數(shù)無法正確地完成這類對象的拷貝。在這種情況下,就需要自己定義類的拷貝構(gòu)造函數(shù),以自定義的方式完成像指針成員變量這樣的需要特殊處理的內(nèi)容的拷貝工作。例如,有一個Computer類,它有一個指針類型的成員變量m_pKeyboard,指向的是一個獨立的Keboard對象,這時就需要定義Compuer類的拷貝構(gòu)造函數(shù)來完成特殊的復(fù)制工作:
// 鍵盤類,因為結(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) // 直接使用初始化屬性列表完成對象類型成員變量m_strModel的復(fù)制 {// 創(chuàng)建新對象,完成指針類型成員變量m_pKeyboard的復(fù)制// 獲得已有對象com的指針類型成員變量m_pKeyboardKeyboard* pOldKeyboard = com.GetKeyboard();// 以pOldKeyboard所指向的Keyboard對象為藍(lán)本,// 創(chuàng)建一個新的Keyboard對象,并讓m_Keyboard指向這個對象if( nullptr != pOldKeyboard )// 這里Keyboard對象的復(fù)制使用的是Keyboard類的默認(rèn)拷貝構(gòu)造函數(shù)m_pKeyboard = new Keyboard(*(pOldKeyboard));elsem_pKeyboard = nullptr; // 如果沒有鍵盤 }// 析構(gòu)函數(shù), // 對于對象類型的成員變量m_strModel,會被自動銷毀,無需在析構(gòu)函數(shù)中進行處理 // 對于指針類型的成員變量m_pKeyboard,則需要在析構(gòu)函數(shù)中主動銷毀 ~Computer() {delete m_pKeyboard;m_pKeyboard = nullptr; }// 成員函數(shù),設(shè)置或獲得鍵盤對象指針void SetKeyboard(Keyboard* pKeyboard){m_pKeyboard = pKeyboard;}Keyboard* GetKeyboard() const{return m_pKeyboard;}private:// 指針類型的成員變量Keyboard* m_pKeyboard;// 對象類型的成員變量string m_strModel; };在這段代碼中,我們?yōu)镃omputer類創(chuàng)建了一個自定義的拷貝構(gòu)造函數(shù)。在這個拷貝構(gòu)造函數(shù)中,對于對象類型的成員變量m_strModel,我們直接使用初始化屬性列表就完成了成員變量的拷貝。而對于指針類型成員變量m_pKeyboard而言,它的拷貝并不是拷貝這個指針的值本身,而應(yīng)該拷貝的是這個指針?biāo)赶虻膶ο蟆K?#xff0c;對于指針類型的成員變量,并不能直接采用內(nèi)存拷貝的形式完成拷貝,那樣只是拷貝了指針的值,而指針?biāo)赶虻膬?nèi)容并沒有得到拷貝。要完成指針類型成員變量的拷貝,首先應(yīng)該獲得已有對象的指針類型成員變量,進而通過它獲得它所指向的對象,然后再創(chuàng)建一個副本并將新對象中相應(yīng)的指針類型成員變量指向這個對象,比如我們這里的“m_pKeyboard = new Keyboard(*(pOldKeyboard));”,這樣才完成了指針類型成員變量的復(fù)制。這個我們自己定義的拷貝構(gòu)造函數(shù)不僅能夠拷貝Computer類的對象類型成員變量m_strModel,也能夠正確地完成指針類型成員變量m_pKeyboard的拷貝,最終才能完成對Computer對象的拷貝。例如:
// 引入斷言所在的頭文件 #include <assert.h>//…// 創(chuàng)建一個Computer對象oldcom Computer oldcom;// 創(chuàng)建oldcom的Keyboard對象并修改其屬性 Keyboard keyboard;keyboard.m_strModel = "Microsoft-101";// 將鍵盤組裝到oldcom上 oldcom.SetKeyboard(&keyboard);// 以oldcom為藍(lán)本,利用Computer類的拷貝構(gòu)造函數(shù)創(chuàng)建新對象newcom// 新的newcom對象是oldcom對象的一個副本 Computer newcom(oldcom);// 使用斷言判斷兩個Computer對象是否相同, // 電腦型號應(yīng)該相同 assert(newcom.GetModel() == oldcom.GetModel()); // 不同的Computer對象應(yīng)該擁有不同的Keyboard對象 assert( newcom.GetKeyboard() != oldcom.GetKeyboard() ); // 因為是復(fù)制,不同的Keyboard對象應(yīng)該是相同的型號 assert( newcom.GetKeyboard()->m_strModel== oldcom.GetKeyboard()->m_strModel );在C++中,除了使用拷貝構(gòu)造函數(shù)創(chuàng)建對象的副本作為新的對象之外,在創(chuàng)建一個新對象之后,還常常將一個已有的對象直接賦值給它來完成新對象的初始化。例如:
// 創(chuàng)建一個新的對象 Computer newcom; // 利用一個已有的對象對其進行賦值,完成初始化 newcom = oldcom;賦值的過程,實際上也是一個拷貝的過程,就是將等號右邊的對象拷貝到等號左邊的對象。跟類的拷貝構(gòu)造函數(shù)相似,如果沒有顯式地為類定義賦值操作符,編譯器也會為其生成一個默認(rèn)的賦值操作符,以內(nèi)存拷貝的方式完成對象的賦值操作。因為同樣是以內(nèi)存拷貝的方式完成對象的復(fù)制,所以當(dāng)類中有指針型成員變量時,也同樣會遇到只能拷貝指針的值而無法拷貝指針?biāo)赶虻膬?nèi)容的問題。因此,要完成帶有指針型成員變量的類對象的賦值,必須對類的賦值操作符進行自定義,在其中以自定義的方式來完成指針型成員變量的復(fù)制。例如,Computer類中含有指針型成員變量m_pKeybard,可以這樣自定義它的賦值操作符來完成其賦值操作。
// 定義了賦值操作符“=”的電腦類 class Computer { public:// 自定義的賦值操作符Computer& operator = (const Computer& com){// 判斷是否是自己給自己賦值// 如果是自賦值,則直接返回對象本身 // 這里的this指針,是類當(dāng)中隱含的一個指向自身對象的指針。if( this == &com ) return *this;// 直接完成對象型成員變量的賦值m_strModel = com.m_strModel;// 創(chuàng)建舊有對象的指針型成員變量所指對象的副本// 并將被賦值對象相應(yīng)的指針型成員變量指向這個副本對象m_pKeyboard = new Keyboard(*(com.GetKeyboard()));}// … };在上面的賦值操作符函數(shù)中,我們首先判斷這是不是一個自賦值操作。所謂自賦值,就是自己給自己賦值。例如:
// 用newcom給newcom賦值 newcom = newcom;嚴(yán)格意義上說,這種自賦值操作是沒有意義的,應(yīng)該算是程序員的一個失誤。但作為一個設(shè)計良好的賦值操作符,應(yīng)該可以檢測出這種失誤并給予恰當(dāng)?shù)奶幚?#xff0c;將程序從程序員的失誤中解救出來:
// 判斷是否是自賦值操作 // 將this指針與傳遞進來的指向com對象的指針進行比較 // 如果相等,就是自賦值操作,直接返回這個對象本身 if( this == &com) return *this;在賦值操作符函數(shù)中如果檢測到這種自賦值操作,它就直接返回這個對象本身從而避免后面的復(fù)制操作。如果不是自賦值操作,對于對象型成員變量,使用“=”操作符直接完成其賦值;而對于指針型成員變量,則采用跟拷貝構(gòu)造函數(shù)相似的方式,通過創(chuàng)建它所指向的對象的副本,并將左側(cè)對象的相應(yīng)指針型成員變量指向這個副本對象來完成其賦值。
另外值得注意的一點是,賦值操作符的返回值類型并不一定是這個類的引用,我們使用void代替也是可以的。例如:
class Computer { public:// 以void作為返回值類型的賦值操作符void operator = (const Computer& com){// … }// … };以上的代碼雖然在語法上正確,也能夠?qū)崿F(xiàn)單個對象的賦值操作,但是卻無法實現(xiàn)如下形式的連續(xù)賦值操作:
Computer oldcom; // Computer newcom1,newcom2; // 連續(xù)賦值 newcom1 = newcom2 = oldcom;連續(xù)的賦值操作符是從右向左開始進行賦值的,所以,上面的代碼實際上就是:
newcom1 = (newcom2 = oldcom );也就是先將oldcom賦值給newcom2(如果返回值類型是void,這一步是可以完成的),然后將“newcom2 = oldcom”的運算結(jié)果賦值給newcom1,而如果賦值操作符的返回值類型是void,也就意味著“newcom2 = oldcom”的運算結(jié)果是void類型,我們顯然是不能將一個void類型數(shù)據(jù)賦值給一個Computer對象的。所以,為了實現(xiàn)上面這種形式的連續(xù)賦值,我們通常以這個類的引用(Computer&)作為賦值操作符的返回值類型,并在其中返回這個對象本身(“return *this”),已備用做下一步的繼續(xù)賦值操作。
初始化列表構(gòu)造函數(shù)
除了我們在上文中介紹的普通構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)之外,為了讓對象的創(chuàng)建形式更加靈活,C++還提供了一種可以接受一個初始化列表(initializer list)為參數(shù)的構(gòu)造函數(shù),因此這種構(gòu)造函數(shù)也被稱為初始化列表構(gòu)造函數(shù)。初始化列表由一對大括號(“{}”)構(gòu)造而成,可以包含任意多個相同類型的數(shù)據(jù)元素。如果我們希望可以通過不定個數(shù)的相同類型數(shù)據(jù)來創(chuàng)建某個對象,比如,一個工資對象管理著不定個數(shù)的工資項目,包括基本工資,獎金,提成,補貼等等,有的人只有基本工資,而有的人全都有,為了創(chuàng)建工資對象形式上的統(tǒng)一,我們就希望這些不定個數(shù)的工資項目都可以用來創(chuàng)建工資對象。這時,我們就需要通過實現(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){// 以容器的形式訪問初始化列表// 獲取其中的工資項目保存到工資類的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};// 王老師既有基本工資還有獎金和補貼 Salary sWang{5000,9500,1003};// 輸出結(jié)果 cout<<"陳老師的工資:"<<sChen.GetTotal()<<endl;cout<<"王老師的工資:"<<sWang.GetTotal()<<endl;return 0; }從這里可以看到,雖然陳老師和王老師的工資項目各不相同,但是通過初始化列表構(gòu)造函數(shù),他們的工資對象都可以以統(tǒng)一的形式創(chuàng)建。而這正是初始化列表的意義所在,它可以讓不同個數(shù)的同類型數(shù)據(jù)以相同的形式作為函數(shù)參數(shù)。換句話說,如果我們希望某個函數(shù)可以接受不定個數(shù)的同類型數(shù)據(jù)為參數(shù),就可以用初始化列表作為參數(shù)類型。例如,我們可以為Salary類添加一個AddSalary()函數(shù),用初始化列表作為參數(shù),它就可以向Salary對象添加不定個數(shù)的工資項目:
// 以初始化列表為參數(shù)的普通函數(shù) void AddSalary(initializer_list<int> s) {for(int i : s)m_vecSalary.push_back(i); }// …// 后來發(fā)現(xiàn)是陳老師的獎金和補貼忘了計算了,給他加上 // 這里的大括號{}就構(gòu)成初始化列表 sChen.AddSalary({8200,6500});?
6.2.5? 操作符重載
如果要想對兩個對象進行操作,比如兩個對象相加,最直觀的方式就是像數(shù)學(xué)式子一樣,用表示相應(yīng)意義的操作符來連接兩個對象,以此表達對這兩個對象的操作。在本質(zhì)上,操作符就相當(dāng)于一個函數(shù),它有自己的參數(shù),可以用來接收操作符所操作的數(shù)據(jù);也有自己的函數(shù)名,就是操作符號;同時也有返回值,用于返回結(jié)果數(shù)據(jù)。而在使用上,只需要用操作符連接被操作的兩個對象,比函數(shù)調(diào)用簡單直觀得多,代碼的可讀性更好。所以在表達一些常見的操作時,比如對兩個對象的加減操作,我們往往通過重載這個類的相應(yīng)意義的操作符來完成。在C++中有許多內(nèi)置的數(shù)據(jù)類型,包括int、char、string等,而這些內(nèi)置的數(shù)據(jù)類型都有許多已經(jīng)定義的操作符可以用來表達它們之間的操作。比如,我們可以用“+”操作符來表達兩個對象之間的“加和”操作,用它連接兩個int對象,得到的“加和”操作結(jié)果就是這兩個數(shù)的和,而用它連接兩個string對象,得到的“加和”操作結(jié)果就是將兩個字符串連接到一起。例如:
int a = 3; int b = 4; // 使用加法操作符“+”獲得兩個int類型變量的和 int c = a + b; cout<<a<<" + "<<b<< " = "<<c<<endl;string strSub1("Hello "); string strSub2("C++"); // 使用加法操作符“+”獲得兩個string類型變量的連接結(jié)果 string strCombin = strSub1 + strSub2; cout<<strSub1<<"+ "<<strSub2<<" = "<<strCombin<<endl;這種用操作符來表達對象之間的操作關(guān)系的方式,用抽象性的操作符表達了具體的操作過程,從而隱藏了操作過程的具體細(xì)節(jié),既直觀又自然,也就更便于使用。對于內(nèi)置的數(shù)據(jù)類型,C++已經(jīng)提供了豐富的操作符供我們選擇使用以完成常見的操作,比如表示數(shù)學(xué)運算的“+”(加)、“-”(減)、“*”(乘)、“/”(除)。但是對于我們新定義的類而言,其兩個對象之間是不能用這些操作符直接進行操作的。比如,分別定義了Father類和Mother類的兩個對象,我們希望可以用加法操作符“+”連接這兩個對象,進而通過運算得出一個Baby類的對象:
// 分別定義Father類和Mother類的對象 Father father; Mother mother;// 用加法操作符“+”連接兩個對象,運算得到Baby類的對象 Baby baby = father + mother;以上語句所表達的是一件顯而易見的事情,但是,如果沒有對Father類的加法操作符“+”進行定義,Father類是不知道如何和一個Mother類的對象加起來創(chuàng)建一個Baby類對象的,這樣的語句會出現(xiàn)編譯錯誤,一件顯而易見的事情在C++中卻行不通。但幸運的是,C++允許我們對這些操作符進行重載,讓我們可以對操作符的行為進行自定義。既然是自定義,自然是想干啥就干啥,自然也就可以讓Father類對象加上Mother類對象得到Baby類對象,讓上面的代碼成為可能。
在功能上,重載操作符等同于類的成員函數(shù),兩者并無本質(zhì)上的差別,可以簡單地將重載操作符看成是一類比較特殊的成員函數(shù)。雖然成員函數(shù)可以提供跟操作符相同的功能,但是運用操作符可以讓語句更加自然簡潔,也更具可讀性。比如,“a.add(b)”調(diào)用函數(shù)add()以實現(xiàn)兩個對象a和b相加,但是表達相同意義的“a + b”語句,遠(yuǎn)比“a.add(b)”更直觀也更容易讓人理解。
在C++中,聲明重載操作符的語法格式如下:
class 類名 { public:返回值類型 operator 操作符 (參數(shù)列表){// 操作符的具體運算過程 } };從這里可以看到,重載操作符和類的成員函數(shù)在本質(zhì)上雖然相同,但在形式上還是存在一些細(xì)微的差別。普通成員函數(shù)以標(biāo)識符(不以數(shù)字為首的字符串)作為函數(shù)名,而重載操作符以“operator 操作符”作為函數(shù)名。其中的“operator”表示這是一個重載的操作符函數(shù),而其后的操作符就是我們要定義的符號。
在使用上,當(dāng)使用操作符連接兩個對象進行運算時,實際上相當(dāng)于調(diào)用第一個對象的操作符函數(shù),而第二個對象則作為這個操作符函數(shù)的參數(shù)。例如,使用加法操作符對兩個對象進行運算:
a + b;這條語句實際上等同于:
a.operator + (b);“a + b”表示調(diào)用的是對象a的操作符“operator +”,而對象b則是這個操作符函數(shù)的參數(shù)。理解了這些,要想讓“father + mother”得到baby對象,只需要定義Father類的“+”操作符函數(shù)(因為father位于操作符之前,所以我們定義father所屬的Father類的操作符),使其可以接受一個Mother類的對象作為參數(shù),并返回一個Baby類的對象就可以了:
// 母親類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)建一個Baby對象并返回,省略創(chuàng)建過程…return Baby("MiaoMiao");} };在Father類的重載操作符“+”中,它可以接受一個Mother類的對象作為參數(shù),并在其中創(chuàng)建一個Baby類的對象作為操作符的返回值。這樣就完整地表達了一個Father類的對象加上一個Mother類的對象得到一個Baby類的對象的意義,現(xiàn)在就可以方便地使用操作符“+”將Father類的對象和Mother類的對象相加而得到一個Baby類的對象了。需要注意的是,這里我們只是定義了Father類的“+”操作符,所以在用它計算的時候,只能是Father類的對象放在“+”之前,而如果希望Mother類的對象也可以放在“+”之前,相應(yīng)地就同樣需要定義Mother類的“+”操作符。
轉(zhuǎn)載于:https://www.cnblogs.com/nihaoCPP/p/operator_overload.html
總結(jié)
以上是生活随笔為你收集整理的你好,C++(34)有一只叫做多利的羊 6.2.4 拷贝构造函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xhost和XServer相关概念汇总
- 下一篇: 组合自定义控件的步骤详解