一、頭文件與類的聲明
1.guard(防衛式聲明)
在complex.h文件中寫出:
#ifndef?__COMPLEX__
#define?__COMPLEX__
? ? …
#endif
2.header的布局
0--forward declarations(前置聲明)
1--class declarations(類聲明)
2--class definition(類定義)
3.class declaration
class head
class body
二、構造函數
1.class template
template
class classname
{
public:
? ? ......
private:
? ? T data1,data2;
};
2.inline 函數
(1)內聯的目的:
1. C中使用define這種形式宏定義的原因是因為,C語言是一個效率很高的語言,這種宏定義在形式及使用上像一個函數,但它使用預處理器實現,沒有了參數壓棧,代碼生成等一系列的操作,因此,效率很高,這是它在C中被使用的一個主要原因。
2. 這種宏定義在形式上類似于一個函數,但在使用它時,僅僅只是做預處理器符號表中的簡單替換,因此它不能進行參數有效性的檢測,也就不能享受C++編譯器嚴格類型檢查的好處,另外它的返回值也不能被強制轉換為可轉換的合適的類型,這樣,它的使用就存在著一系列的隱患和局限性。
3. 在C++中引入了類及類的訪問控制,這樣,如果一個操作或者說一個表達式涉及到類的保護成員或私有成員,你就不可能使用這種宏定義來實現(因為無法將this指針放在合適的位置)。
4. inline 推出的目的,也正是為了取代這種表達式形式的宏定義,它消除了宏定義的缺點,同時又很好地繼承了宏定義的優點。
(2)內聯原理:
直接執行替換而不進行一般函數的參數壓棧,在預處理階段即可完成。
1. inline 定義的類的內聯函數,函數的代碼被放入符號表中,在使用時直接進行替換,(像宏一樣展開),沒有了調用的開銷,效率也很高。
2. 很明顯,類的內聯函數也是一個真正的函數,編譯器在調用一個內聯函數時,會首先檢查它的參數的類型,保證調用正確。然后進行一系列的相關檢查,就像對待任何一個真正的函數一樣。這樣就消除了它的隱患和局限性。
3. inline 可以作為某個類的成員函數,當然就可以在其中使用所在類的保護成員及私有成員。
(3)內聯的用法:
函數若在class body內定義完成,便自動成為inline候選。在外部定義的成員函數需要顯式的添加上inline。外部全局函數也是直接在前面加上inline。
內聯函數最重要的使用地方是用于類的存取函數。
(4)內聯成功的條件:
1.沒有分支(循環、開關)沒有函數調用;
2.沒有取函數指針的操作;
3.函數形式簡單。
3.訪問級別
4.構造函數
目的:創建對象是完成初始化。
用法:
1.構造函數的命名必須和類名完全相同;
2.沒有返回值,也不能用void來修飾;
3.構造函數不能被直接調用,必須通過new運算符在創建對象時才會自動調用;
4.構造函數有回滾的效果,構造函數拋出異常時,構造的是一個不完整對象,會回滾,將此不完整對象的成員釋放;
5.如果一個類中沒有定義任何的構造函數,那么編譯器只有在以下三種情況,才會提供默認構造函數:a.如果類有虛擬成員函數或者虛擬繼承父類(即有虛擬基類)時;b.如果類的基類有構造函數(可以是用戶定義的構造函數,或編譯器提供的默認構造函數);c.在類中的所有非靜態的對象數據成員,它們對應的類中有構造函數(可以是用戶定義的構造函數,或編譯器提供的默認構造函數)。
5.構造函數可以有很多個(操作符重載-1,overloading)
事實上重載會以函數名加參數名組合一起命名。
三、參數傳遞與返回值
1.單例模式(singleton)
1.私有構造函數不能通過new關鍵字來創建其對象,一個應用是單例模式(singleton);
2.單例模式的要點有三個;一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。
2.const member functions(常量成員函數)
返回類型 成員函數 const { ... };//function內部不允許改變本類的數據
3.參數傳遞
(pass by value ?or ?pass by reference (Is or not to const?))
一般,基本類型用pass by value;復雜類型用pass by reference。盡可能保證參數傳遞較少的值;
4.返回值傳遞
(return by value?or?return?by reference(Is or not to const?))
1.盡可能保證返回值傳遞較少的值;
2.不可返回local變量的引用。牢記作用域!
5.友元函數
1.可以自由取得friend的private成員。
2.相同class的不同object互為友元。
class?complex
{
public:
? ? complex (double r = 0, double i = 0) : re(r), im(i){ }
? ? int func(const?complex & param)?{ return?param.re + param.im; }
private:
? ? double re, im;
};
{
? ? complex c1(2, 1);
? ? complex c2;
? ? c2.func(c1);
}
這里c2可以直接取得c1的私有變量。
6.classbody外的各種定義
注意不能傳遞局部變量的引用
doassignment?plus
inline?complex &?__doapl?(complex * ths, const comples & r)
{
? ? ths->re += r.re;
? ? ths->im += r.im;
? ? return *ths;
}
inline?complex &?complex::operator?+= (const complex&r)
{
? ? return __dopal (this, r);
}
四.操作符重載
1.operator overloading(操作符重載-1,成員函數)有this
inline?complex &?__doapl?(complex * ths, const comples & r)
{
? ? ths->re += r.re;
? ? ths->im += r.im;
? ? return *ths;
}
inline?complex &?complex::operator?+= (const complex& r)
{
? ? return __dopal (this, r);
}
inline?complex &?complex::operator += (this, const complex& r)//this不能寫出來
{
? ? return __dopal (this, r);
}
{
? ? complexc1(2, 1);
? ? complexc2(5);
? ? c2 += c1;//操作到c2
}
2.returnby reference語言分析
傳遞著無需知道接收者是以reference形式接收
inlinecomplex &?__doapl?(complex * ths, const comples & r)
{
? ? ...
? ? return*ths;
}
inlinecomplex &?complex::operator?+= (const complex& r)
{
? ? return __dopal?(this, r);
}
這是相對于用指針的一大優勢
3.operatoroverloading(操作符重載-2,非成員函數)無this
inline?complex?operator?+ (const complex& x, const complex& y)
{
? ? return complex (real(x) + real(y),?imag(x) + imag(y));
}
inline?complex?operator?+ (const complex& x, double y)
{
? ? return comple(real(x) + y, imag(x));
}
inline?complex?operartor?+ (double x, const complex& y)
{
? ? return complex(x + real(y), imag(y));
}
4.臨時對象typename();
//這里一定不能return by reference
inline complex operator + (const complex& x, const complex& y)
{
? ? return complex (real(x) + real(y),?imag(x) + imag(y));//這個返回是一個local?object
}
{
? ? int(7);
? ? complex c1(2, 1);
? ? complex c2;
? ? complex();
? ? comple(4, 5);//這兩個臨時對象到下面就沒了
? ? cout << complex(2);
}
5.一元運算符的重載
inline?complex?operator?+ (const comple& x)
{
? ? return x;
}
inline?complex?operator?- (const complex& x)
{
? ? return complex(-real(x), -imag(x));
}
{
? ? complex c1(2, 1);
? ? complex c2;
? ? cout << -c1;
? ? cout << +c1;
}
其它:
兩個復數的等于,不等于,共軛復數,模運算,極坐標到笛卡爾坐標的轉換
inline?complex?conj?(const complex&x)
{
? ? return complex (real(x), -imag(x));
}
ostream&?operator?<< (ostream& os, const complex& x)
{
? ? return os << '(' << real(x) << ',' <<?imag(x) << ')';
}
設計一個類小結:
1.成員函數盡量放到public,數據盡量放到private
2.函數參數傳遞盡量考慮pass by reference;加不加const要考慮
3.返回值盡量以return by reference,除非需要返回臨時對象
4.成員函數如果不改變數據值的,函數body大括號前要加const
5.盡量使用initializationlist初始化構造函數和成員函數的值
1.構造一個類的步驟:
a.防衛式聲明別忘記;
b.這個類有哪些數據,什么類型的?寫進private區;
c.構造函數怎么寫?參數是否要有默認值?參數是否有必要returnby reference?沒有返回類型。寫進public區。
d.是否要有取得私有數據值的函數的成員函數,寫進public區。返回類型和參數return by reference?不改變成員變量所有要在類成員函數加const?
e.是否需要重載一些操作符?寫成全局函數形式還是成員函數形式?(全局形式可以完成更加泛型一些的操作,如果確定該重載操作符只與該對象本身有關,就攜程成員函數)
f.是否需要友元以便直接訪問私有數據?
2.類名后面加小括號可以創建臨時對象,可以用于返回值。
3.當該對象與其他類型使用此重載操作符的的時候,需要全局函數重載。
4.盡量要寫<<的重載。
?