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