第4章 基于對象的程式設計風格
這一章,我們會設計并屬于實作我們自己的class。
在之前的幾章,我們已經指定Class的一些相關事項。
1、使用Class之前,要包含相應的頭檔案
2、class名稱被視為一個類型,就像int,double一樣。
3、class會提供一組操作函數,讓我們作用于其object上。
4、class由兩部分組成:一組公開的操作函數;一組私有的實作細節。
4.1 如何實作一個Class
我們從實作一個 棧(stack)開始,實作一個class。
什麼是棧呢,棧是一種存放資料的結構,它允許我們在裡面存放數值,并以 後進先出的順序取出。我們以pushing 方式存入數值,以popping方式取出數值。
使用者可能還需要其他操作,如查詢stack空間是否已滿(full),是否為空(empty),查詢stack元素個數(size)。
Class的聲明以關鍵字 class開始,随後接一個class名稱:
class stack;
class的定義類似這樣:
class Stack {
public:
//…public接口
private:
//…private實作部分
};
class 定義有兩部分:class聲明,主體。
主體中的public和private是通路權限,public 可被程式任何地方通路,private隻能被class内部或class friend 通路。
stack class的定義如下:
class Stack{
public:
bool push(const string&);
bool pop(string &elem);
bool peek(string &elem);
bool empty();
bool full();
int size() { return _stack.size();}
private:
vector<string> _stack;
};
所有的member function都要在Class内聲明,如果在Class内定義,則會自動被視為inline函數。在Class外定義,必須用特殊的文法:
inline bool
Stack::empty()
{
return _stack.empty();
}
bool
Stack::pop(string &elem)
{
if(empty())
return false;
elem = _stack.back();
_stack.pop_back();
return true;
}
Inline函數和Class定義 都放在對應的,h檔案中。
非inline函數應該放在和class同名的.cpp檔案中。
下面是Stack member function 的定義。
inline bool Stack::full()
{return _stack.size() == _stack.max_size();}
bool Stack::peek(string &elem)
{
if(empty())
return false;
elem = _stack.back();
return true;
}
bool Stack::push(const string &elem)
{
if(full())
return false;
_stack.push_back(elem);
return true;
}
4.2 構造函數和析構函數
編譯器會在每次class object被定義時,調用構造函數(constructor)來進行初始化。
constructor(構造函數)的名稱必須和Class名稱相同。constructor沒有傳回值類型,可以被重載。
最簡單的constructor是default constructor,它不需要接受參數。
參數表為空 或者為每個參數提供預設值。
Member initialization List(成員初始化清單)
Triangular::Triangular(const Triangular &rhs)
:_length(rhs._length),_beg_pos(rhs._beg_pos),_next(rhs._beg_pos-1)
{}
Member initialization list 緊接在參數清單的最後的冒号後面,是個以逗号分隔的清單。
destructor(析構函數)
析構函數是class名稱加上~字首,沒有傳回值,也沒有參數。用來釋放對象的資源(釋放記憶體。析構函數由系統自動調用。
Memberwise initialization(成員逐一初始化)
即當使用一個對象給另一個對象初始化時,對象中的成員會逐一複制。
有時預設的複制操作不符合我們的要求,我們要自定義copy constructor。
類似于:
Matrix:Matrix(const Martrix &rhs){
}
4,3 mutable(可變)可const(不變)
class 設計者在Member function身上标注const,告訴編譯器,這個Member function 不會更改Class Object中的内容。
const修飾符寫于函數參數清單之後,凡是在Class主體以外定義者,如果他是一個const member function ,它必須在聲明和定義處都指定const。
Member function 可以根據const與否而重載,是以可以設計這樣的重載函數:
const BigClass& val() cosnt(return _val);
BigClass& val(){return _val};
針對const的mutable, 将某個成員标示為mutable,就可以宣稱,對這個成員的修改不會破壞class object的常量性。(在const member function中可以修改這個成員)
4.4 this指針
this指針時Member function内用來指向其調用者的指針。
4.5 靜态類成員
static(靜态)data member用來表示唯一的,可以共享的Member。它可以在同一類的所有對象中被通路。
對Class而言,static data member隻有唯一的一份實體。是以我們必須在代碼檔案中提供清楚的定義。
//.cpp
vector<int> Triangular::elems;
如果在class member function内部通路static data member,其通路方式和通路一般資料成員相同。
const static int data member可以在聲明時指定初值。
Static Member function(靜态成員函數)
static可以不用任何具體的對象調用:
如 Triangular::is_elem(7);
并且為了不和具體的對象有關,static Member function 不能通路non-static member。
static Member function的聲明方式是在原函數前加關鍵字static。
在Class主體外進行定義時,無需再加static(此規則也适用于static data member)。
,
4.6 打造一個Iterator Class
我們可以像定義Member function那樣定義運算符,運算符函數和普通函數很像,差別是它的名稱就是operator加運算符号。
bool operator==(const Triangular_iterator &) const;
int operatir*()const;
運算符重載的規則:
1、不能引入新的運算符
2、運算符操作數個數不可變
3、運算符優先級不變
4、運算符函數的參數清單至少有一個是class類型的。
運算符定義的方式可以向Member function一樣,
也可以像non member function一樣
Non member function 運算符的參數清單中,一定會比Member運算符多一個參數,也就是this指針。對于Member運算符來說,這個this指針隐式代表左操作數。
前++ 和後++
前++的參數表是空的,
後++的參數表得有一個int參數 inline Triangula_iterator Tirangular_iterator::
operator++(int)
{..
return …;
}
但使用時不需要傳入這個int參數,編譯器自動設定為0。
直接使用
it++;
即可。
4.7 friend 友類
class可以将其他function或class指定為friend,這樣就可以讓他們具備和class member function相同的通路權限,可以通路class 的private member。
隻要在某個函數的原型前加上關鍵字friend就可以将它聲明為某個class的關鍵字。
如果讓class A 認為class B是自己的friend,則class B的所有函數都是A的friend。
4.8實作複制運算符(copy assignment operator)
隻要為Class提供copy assignmemnt operator,它就會被用來取代預設的memberwise copy。
4.9 實作一個function object
function object 是一種提供有function call運算符的class。
通常我們将function object 作為參數傳遞給泛型算法。
function call運算符:
例:
inline bool LessThan::operator()(int value) const {return value<_val;}
4.10 重載iostream運算符
為了讓我們的class支援
cout<<train<<endl;這種形式,我們需要重載iostream運算符。
ostream & operator<<(ostream &os, const Triangular &rhs)
{
os<<”(“<<rhs._beg_pos()<<”,”<<rhs.length()<<”,”;
rhs.display(rhs.length(),rhs._beg_pos(),os);
return os;
}
類似的可以重載>>運算符。
4.11 指針,指向Class Member Function
指向成員函數的指針和指向普通函數的指針很像,都需要指定傳回類型和參數清單。
不過指向成員函數的指針還要指定 所屬的class
如:
void (num_sequence::*pm)(int) = 0;