1.constructor構(gòu)造函數(shù)
構(gòu)造函數(shù)名稱=類名。構(gòu)造函數(shù)的作用:初始化對(duì)象的數(shù)據(jù)成員。
1.1 構(gòu)造函數(shù)的種類
引自:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html
class Complex
{
private :
double? ? m_real;
double? ? m_imag;
public:
//? ? 無參數(shù)構(gòu)造函數(shù)
// 如果創(chuàng)建一個(gè)類你沒有寫任何構(gòu)造函數(shù),則系統(tǒng)會(huì)自動(dòng)生成默認(rèn)的無參構(gòu)造函數(shù),函數(shù)為空,什么都不做
// 只要你寫了一個(gè)下面的某一種構(gòu)造函數(shù),系統(tǒng)就不會(huì)再自動(dòng)生成這樣一個(gè)默認(rèn)的構(gòu)造函數(shù),如果希望有一個(gè)這樣的無參構(gòu)造函數(shù),則需要自己顯示地寫出來
Complex(void)
{
m_real = 0.0;
m_imag = 0.0;
}
//? ? 一般構(gòu)造函數(shù)(也稱重載構(gòu)造函數(shù))
// 一般構(gòu)造函數(shù)可以有各種參數(shù)形式,一個(gè)類可以有多個(gè)一般構(gòu)造函數(shù),前提是參數(shù)的個(gè)數(shù)或者類型不同(基于c++的重載函數(shù)原理)
// 例如:你還可以寫一個(gè) Complex( int num)的構(gòu)造函數(shù)出來
// 創(chuàng)建對(duì)象時(shí)根據(jù)傳入的參數(shù)不同調(diào)用不同的構(gòu)造函數(shù)
Complex(double real, double imag)
{
m_real = real;
m_imag = imag;
}
//? ? 復(fù)制構(gòu)造函數(shù)(也稱為拷貝構(gòu)造函數(shù))
//? ? 復(fù)制構(gòu)造函數(shù)參數(shù)為類對(duì)象本身的引用,用于根據(jù)一個(gè)已存在的對(duì)象復(fù)制出一個(gè)新的該類的對(duì)象,一般在函數(shù)中會(huì)將已存在對(duì)象的數(shù)據(jù)成員的值復(fù)制一份到新創(chuàng)建的對(duì)象中
//? ? 若沒有顯示的寫復(fù)制構(gòu)造函數(shù),則系統(tǒng)會(huì)默認(rèn)創(chuàng)建一個(gè)復(fù)制構(gòu)造函數(shù),但當(dāng)類中有指針成員時(shí),由系統(tǒng)默認(rèn)創(chuàng)建該復(fù)制構(gòu)造函數(shù)會(huì)存在風(fēng)險(xiǎn),具體原因請(qǐng)查詢 有關(guān) “淺拷貝” 、“深拷貝”的文章論述
Complex(const Complex & c)
{
// 將對(duì)象c中的數(shù)據(jù)成員值復(fù)制過來
m_real = c.m_real;
m_imag? ? = c.m_imag;
}
// 類型轉(zhuǎn)換構(gòu)造函數(shù),根據(jù)一個(gè)指定的類型的對(duì)象創(chuàng)建一個(gè)本類的對(duì)象,
//需要注意的一點(diǎn)是,這個(gè)其實(shí)就是一般的構(gòu)造函數(shù),但是對(duì)于出現(xiàn)這種單參數(shù)的構(gòu)造函數(shù),C++會(huì)默認(rèn)將參數(shù)對(duì)應(yīng)的類型轉(zhuǎn)換為該類類型,有時(shí)候這種隱私的轉(zhuǎn)換是我們所不想要的,所以需要使用explicit來限制這種轉(zhuǎn)換。
// 例如:下面將根據(jù)一個(gè)double類型的對(duì)象創(chuàng)建了一個(gè)Complex對(duì)象
Complex(double r)
{
m_real = r;
m_imag = 0.0;
}
// 等號(hào)運(yùn)算符重載(也叫賦值構(gòu)造函數(shù))
// 注意,這個(gè)類似復(fù)制構(gòu)造函數(shù),將=右邊的本類對(duì)象的值復(fù)制給等號(hào)左邊的對(duì)象,它不屬于構(gòu)造函數(shù),等號(hào)左右兩邊的對(duì)象必須已經(jīng)被創(chuàng)建
// 若沒有顯示的寫=運(yùn)算符重載,則系統(tǒng)也會(huì)創(chuàng)建一個(gè)默認(rèn)的=運(yùn)算符重載,只做一些基本的拷貝工作
Complex &operator=( const Complex &rhs )
{
// 首先檢測(cè)等號(hào)右邊的是否就是左邊的對(duì)象本身,若是本對(duì)象本身,則直接返回
if ( this == &rhs )
{
return *this;
}
// 復(fù)制等號(hào)右邊的成員到左邊的對(duì)象中
this->m_real = rhs.m_real;
this->m_imag = rhs.m_imag;
// 把等號(hào)左邊的對(duì)象再次傳出
// 目的是為了支持連等 eg:? ? a=b=c 系統(tǒng)首先運(yùn)行 b=c
// 然后運(yùn)行 a= ( b=c的返回值,這里應(yīng)該是復(fù)制c值后的b對(duì)象)
return *this;
}
};
下面使用上面定義的類對(duì)象來說明各個(gè)構(gòu)造函數(shù)的用法:
int main()
{
// 調(diào)用了無參構(gòu)造函數(shù),數(shù)據(jù)成員初值被賦為0.0
Complex c1,c2;
// 調(diào)用一般構(gòu)造函數(shù),數(shù)據(jù)成員初值被賦為指定值
Complex c3(1.0,2.5);
// 也可以使用下面的形式
Complex c3 = Complex(1.0,2.5);
//? ? 把c3的數(shù)據(jù)成員的值賦值給c1
//? ? 由于c1已經(jīng)事先被創(chuàng)建,故此處不會(huì)調(diào)用任何構(gòu)造函數(shù)
//? ? 只會(huì)調(diào)用 = 號(hào)運(yùn)算符重載函數(shù)
c1 = c3;
//? ? 調(diào)用類型轉(zhuǎn)換構(gòu)造函數(shù)
//? ? 系統(tǒng)首先調(diào)用類型轉(zhuǎn)換構(gòu)造函數(shù),將5.2創(chuàng)建為一個(gè)本類的臨時(shí)對(duì)象,然后調(diào)用等號(hào)運(yùn)算符重載,將該臨時(shí)對(duì)象賦值給c1
c2 = 5.2;
// 調(diào)用拷貝構(gòu)造函數(shù)( 有下面兩種調(diào)用方式)
Complex c5(c2);
Complex c4 = c2;? // 注意和 = 運(yùn)算符重載區(qū)分,這里等號(hào)左邊的對(duì)象不是事先已經(jīng)創(chuàng)建,故需要調(diào)用拷貝構(gòu)造函數(shù),參數(shù)為c2
//這一點(diǎn)特別重要,這兒是初始化,不是賦值。其實(shí)這兒就涉及了C++中的兩種初始化的方式:復(fù)制初始化和賦值初始化。其中c5采用的是復(fù)制初始化,而c4采用的是賦值初始化,這兩種方式都是要調(diào)用拷貝構(gòu)造函數(shù)的。
}
1.2隨堂筆記
構(gòu)造函數(shù)都有initialization list(初始化列表),寫代碼時(shí)應(yīng)充分利用初始化列表,使用初始化列表比在函數(shù)體內(nèi)初始化成員變量效率高。
函數(shù)可以overloading(重載),允許同名函數(shù),但是實(shí)參不能一樣,但是在編譯后的名稱實(shí)際是不一樣的,不存在編譯后名稱一樣的兩個(gè)函數(shù)。函數(shù)重載常常發(fā)生在構(gòu)造函數(shù)。
已有一個(gè)構(gòu)造函數(shù),實(shí)參均有默認(rèn)值,則不能再寫一個(gè)無參數(shù)構(gòu)造函數(shù)。兩者均可為默認(rèn)構(gòu)造函數(shù),實(shí)際上有沖突。
constructor放在private區(qū)域: singleton 單例模式
2.C++運(yùn)算符重載
2.1C++運(yùn)算符重載
C++中預(yù)定義的運(yùn)算符的操作對(duì)象只能是基本數(shù)據(jù)類型。但實(shí)際上,對(duì)于許多用戶自定義類型(例如類),也需要類似的運(yùn)算操作。這時(shí)就必須在C++中重新定義這些運(yùn)算符,賦予已有運(yùn)算符新的功能,使它能夠用于特定類型執(zhí)行特定的操作。運(yùn)算符重載的實(shí)質(zhì)是函數(shù)重載,它提供了C++的可擴(kuò)展性,也是C++最吸引人的特性之一。
運(yùn)算符重載是通過創(chuàng)建運(yùn)算符函數(shù)實(shí)現(xiàn)的,運(yùn)算符函數(shù)定義了重載的運(yùn)算符將要進(jìn)行的操作。運(yùn)算符函數(shù)的定義與其他函數(shù)的定義類似,惟一的區(qū)別是運(yùn)算符函數(shù)的函數(shù)名是由關(guān)鍵字operator和其后要重載的運(yùn)算符符號(hào)構(gòu)成的。運(yùn)算符函數(shù)定義的一般格式如下:
<返回類型說明符>operator<運(yùn)算符符號(hào)>(<參數(shù)表>)
{
<函數(shù)體>
}
運(yùn)算符重載時(shí)要遵循以下規(guī)則:
(1)?除了類屬關(guān)系運(yùn)算符"."、成員指針運(yùn)算符".*"、作用域運(yùn)算符"::"、sizeof運(yùn)算符和三目運(yùn)算符"?:"以外,C++中的所有運(yùn)算符都可以重載。
(2)?重載運(yùn)算符限制在C++語言中已有的運(yùn)算符范圍內(nèi)的允許重載的運(yùn)算符之中,不能創(chuàng)建新的運(yùn)算符。
(3)?運(yùn)算符重載實(shí)質(zhì)上是函數(shù)重載,因此編譯程序?qū)\(yùn)算符重載的選擇,遵循函數(shù)重載的選擇原則。
(4)?重載之后的運(yùn)算符不能改變運(yùn)算符的優(yōu)先級(jí)和結(jié)合性,也不能改變運(yùn)算符操作數(shù)的個(gè)數(shù)及語法結(jié)構(gòu)。
(5)?運(yùn)算符重載不能改變?cè)撨\(yùn)算符用于內(nèi)部類型對(duì)象的含義。它只能和用戶自定義類型的對(duì)象一起使用,或者用于用戶自定義類型的對(duì)象和內(nèi)部類型的對(duì)象混合使用時(shí)。
(6)?運(yùn)算符重載是針對(duì)新類型數(shù)據(jù)的實(shí)際需要對(duì)原有運(yùn)算符進(jìn)行的適當(dāng)?shù)母脑欤剌d的功能應(yīng)當(dāng)與原有功能相類似,避免沒有目的地使用重載運(yùn)算符。
運(yùn)算符函數(shù)重載一般有兩種形式:重載為類的成員函數(shù)和重載為類的非成員函數(shù)。非成員函數(shù)通常是友元。(可以把一個(gè)運(yùn)算符作為一個(gè)非成員、非友元函數(shù)重載。但是,這樣的運(yùn)算符函數(shù)訪問類的私有和保護(hù)成員時(shí),必須使用類的公有接口中提供的設(shè)置數(shù)據(jù)和讀取數(shù)據(jù)的函數(shù),調(diào)用這些函數(shù)時(shí)會(huì)降低性能。可以內(nèi)聯(lián)這些函數(shù)以提高性能。)
成員函數(shù)運(yùn)算符
運(yùn)算符重載為類的成員函數(shù)的一般格式為:
<函數(shù)類型>operator<運(yùn)算符>(<參數(shù)表>)
{
<函數(shù)體>
}
當(dāng)運(yùn)算符重載為類的成員函數(shù)時(shí),函數(shù)的參數(shù)個(gè)數(shù)比原來的操作數(shù)要少一個(gè)(后置單目運(yùn)算符除外),這是因?yàn)槌蓡T函數(shù)用this指針隱式地訪問了類的一個(gè)對(duì)象,它充當(dāng)了運(yùn)算符函數(shù)最左邊的操作數(shù)。因此:
(1)?雙目運(yùn)算符重載為類的成員函數(shù)時(shí),函數(shù)只顯式說明一個(gè)參數(shù),該形參是運(yùn)算符的右操作數(shù)。
(2)?前置單目運(yùn)算符重載為類的成員函數(shù)時(shí),不需要顯式說明參數(shù),即函數(shù)沒有形參。
(3)?后置單目運(yùn)算符重載為類的成員函數(shù)時(shí),函數(shù)要帶有一個(gè)整型形參。
調(diào)用成員函數(shù)運(yùn)算符的格式如下:
<對(duì)象名>.operator<運(yùn)算符>(<參數(shù)>)
它等價(jià)于
<對(duì)象名><運(yùn)算符><參數(shù)>
例如:a+b等價(jià)于a.operator+(b)。一般情況下,我們采用運(yùn)算符的習(xí)慣表達(dá)方式。
友元函數(shù)運(yùn)算符
運(yùn)算符重載為類的友元函數(shù)的一般格式為:
friend<函數(shù)類型>operator<運(yùn)算符>(<參數(shù)表>)
{
<函數(shù)體>
}
當(dāng)運(yùn)算符重載為類的友元函數(shù)時(shí),由于沒有隱含的this指針,因此操作數(shù)的個(gè)數(shù)沒有變化,所有的操作數(shù)都必須通過函數(shù)的形參進(jìn)行傳遞,函數(shù)的參數(shù)與操作數(shù)自左至右一一對(duì)應(yīng)。
調(diào)用友元函數(shù)運(yùn)算符的格式如下:
operator<運(yùn)算符>(<參數(shù)1>,<參數(shù)2>)
它等價(jià)于
<參數(shù)1><運(yùn)算符><參數(shù)2>
例如:a+b等價(jià)于operator+(a,b)。
兩種重載形式的比較
在多數(shù)情況下,將運(yùn)算符重載為類的成員函數(shù)和類的友元函數(shù)都是可以的。但成員函數(shù)運(yùn)算符與友元函數(shù)運(yùn)算符也具有各自的一些特點(diǎn):
(1)?一般情況下,單目運(yùn)算符最好重載為類的成員函數(shù);雙目運(yùn)算符則最好重載為類的友元函數(shù)。
(2)?以下一些雙目運(yùn)算符不能重載為類的友元函數(shù):=、()、[]、->。
(3)?類型轉(zhuǎn)換函數(shù)只能定義為一個(gè)類的成員函數(shù)而不能定義為類的友元函數(shù)。
(4)?若一個(gè)運(yùn)算符的操作需要修改對(duì)象的狀態(tài),選擇重載為成員函數(shù)較好。
(5)?若運(yùn)算符所需的操作數(shù)(尤其是第一個(gè)操作數(shù))希望有隱式類型轉(zhuǎn)換,則只能選用友元函數(shù)。
(6)?當(dāng)運(yùn)算符函數(shù)是一個(gè)成員函數(shù)時(shí),最左邊的操作數(shù)(或者只有最左邊的操作數(shù))必須是運(yùn)算符類的一 個(gè)類對(duì)象(或者是對(duì)該類對(duì)象的引用)。如果左邊的操作數(shù)必須是一個(gè)不同類的對(duì)象,或者是一個(gè)內(nèi)部 類型的對(duì)象,該運(yùn)算符函數(shù)必須作為一個(gè)友元函數(shù)來實(shí)現(xiàn)。
(7)?當(dāng)需要重載運(yùn)算符具有可交換性時(shí),選擇重載為友元函數(shù)。
2.2隨堂筆記
complex c1(2,1);
complex c2(5);
c2 += c1;
操作符作用在左邊的操作數(shù)上。
class的成員函數(shù)帶一個(gè)隱藏參數(shù),this指針,不能再代碼中體現(xiàn),this指針參數(shù)位置不一定是第一個(gè),取決于編譯器。
為了滿足 c3 += c2 += c1;重載 += 時(shí)必須有返回值;
3.參數(shù)傳遞與返回值
const member functions 常量成員函數(shù)
對(duì)于class里面的函數(shù),分為會(huì)改變class內(nèi)數(shù)據(jù)內(nèi)容的和不會(huì)改變class內(nèi)數(shù)據(jù)內(nèi)容。
不會(huì)改變class內(nèi)數(shù)據(jù)內(nèi)容的函數(shù),馬上加上const。
參數(shù)傳遞 pass by value vs. pass by reference(to const)
傳reference底層就是傳指針,速度快,形式好看,如果不想被改變,則加上const。
返回值傳遞,pass by value vs. pass by reference(to const)
對(duì)于函數(shù)體內(nèi)創(chuàng)建的local object,不能return by reference,因?yàn)樗纳芷谠诤瘮?shù)返回后就終止了。此時(shí)若用reference返回,則會(huì)返回壞的內(nèi)容。
friend(友元函數(shù))
友元函數(shù)可以直接訪問class的private數(shù)據(jù)。
相同class的各個(gè)objects互為friends(友元)。
寫一個(gè)class應(yīng)該注意的地方。
1、數(shù)據(jù)一定放在private里面。
2、參數(shù)盡量以reference來傳遞,看情況加上const。
3、返回值盡量也以reference來傳遞,可以的情況下。
4、class內(nèi)的各種函數(shù),需要加const的一定要加上const。
5、構(gòu)造函數(shù)的初始化數(shù)據(jù)使用initialization list。