八、this指標、靜態成員、成員物件和封閉類
1、this指標
1)this指標的作用
其作用就是指向成員函式所作用 的物件
非靜態成員函式中可以直接使用this來代表指向該函式作用的物件的指標。
2)this指標和靜態成員函式
靜態成員函式中不能使用 this 指標!
因為靜態成員函式並不具體作用與某個物件!
因此,靜態成員函式的真實的引數的個數,就是程 序中寫出的引數個數!
非靜態成員函式的引數個數=所寫引數個數+1;
2、靜態成員
1)基本概念
靜態成員:在說明前面加了static關鍵字的成員。
例如:
class CRectangle { private: int w, h; static int nTotalArea; //靜態成員變數 static int nTotalNumber; public: CRectangle(int w_,int h_); ~CRectangle(); static void PrintTotal(); //靜態成員函式 };
普通成員變數每個物件有各自的一份,而靜態成員變數一共就一份,為所有物件共享。
注意:sizeof 運算子不會計算靜態成員變數。
普通成員函式必須具體作用於某個物件,而靜態成員 函式並不具體作用於某個物件。
因此靜態成員不需要通過物件就能訪問。
靜態成員變數本質上是全域性變數,哪怕一個物件都不存在,類 的靜態成員變數也存在。
靜態成員函式本質上是全域性函式。
設定靜態成員這種機制的目的是將和某些類緊密相關的全域性變 量和函式寫到類裡面,看上去像一個整體,易於維護和理解。
2)如何訪問靜態成員
方法1:類名::成員名
方法2:物件名.成員名
方法3:指標->成員名
方法4:引用名.成員名
3)靜態成員示例
考慮一個需要隨時知道矩形總數和總面積的圖形處理程式,可以用全域性變數來記錄總數和總面積
用靜態成員將這兩個變數封裝進類中,就更容易 理解和維護
class CRectangle { private: int w, h; static int nTotalArea; static int nTotalNumber;public: CRectangle(int w_,int h_); ~CRectangle(); static void PrintTotal(); }; CRectangle::CRectangle(int w_,int h_) { w = w_; h = h_; nTotalNumber ++; nTotalArea += w * h; } CRectangle::~CRectangle() { nTotalNumber --; nTotalArea -= w * h; } void CRectangle::PrintTotal() { cout << nTotalNumber << "," << nTotalArea << endl; } int CRectangle::nTotalNumber = 0; int CRectangle::nTotalArea = 0; // 必須在定義類的檔案中對靜態成員變數進行一次說明 //或初始化。否則編譯能通過,連結不能通過。 int main() { CRectangle r1(3,3), r2(2,2); //cout << CRectangle::nTotalNumber; // Wrong , 私有 CRectangle::PrintTotal(); r1.PrintTotal(); return 0; }
輸出結果:
2,13
2,13
4)注意事項
- 在靜態成員函式中,不能訪問非靜態成員變數, 也不能呼叫非靜態成員函式。
- 在使用CRectangle類時,有時會呼叫複製建構函式 生成臨時的隱藏的CRectangle物件
用一個以CRectangle類物件作為引數的函式時, 呼叫一個以CRectangle類物件作為返回值的函式時
臨時物件在消亡時會呼叫解構函式,減少nTotalNumber 和 nTotalArea的值,可是這些臨時物件在生成時卻沒有增加 nTotalNumber 和 nTotalArea的值。
解決辦法:為CRectangle類寫一個複製建構函式。
CRectangle :: CRectangle(CRectangle & r )
{ w = r.w; h = r.h; nTotalNumber ++; nTotalArea += w * h; }
3、成員物件和封閉類
1)基本概念
成員物件:當一個類的成員是另一個類的物件時,這個物件就叫成員物件。概括的說,就是一個類的成員是一個物件,即成員物件。
有成員物件的類叫封閉(enclosing)類。
class CTyre //輪胎類 { private: int radius; //半徑 int width; //寬度 public: CTyre(int r,int w):radius(r),width(w) { } }; class CEngine //引擎類 { }; class CCar { //汽車類 private: int price; //價格 CTyre tyre; CEngine engine; public: CCar(int p,int tr,int tw ); }; CCar::CCar(int p,int tr,int w):price(p),tyre(tr, w) { }; int main() { CCar car(20000,17,225); return 0; }
上例中,如果 CCar類不定義建構函式, 則下面的語句會編譯出錯:
CCar car;
因為編譯器不明白 car.tyre該如何初始化。car.engine 的初始化沒問題,用預設建構函式即可。
任何生成封閉類物件的語句,都要讓編譯器明白,物件中的成員物件,是如何初始化的。
具體的做法就是:通過封閉類的建構函式的初始化列表。
成員物件初始化列表中的引數可以是任意複雜的表示式,可以包括函式,變數 ,只要表示式中的函式或變數有定義就行。
2)封閉類建構函式和解構函式的執行順序
封閉類物件生成時,先執行所有物件成員的建構函式,然後才執行封閉類的建構函式。
物件成員的建構函式呼叫次序和物件成員在類中的說明次序一致 ,與它們在成員初始化列表中出現的次序無關。
當封閉類的物件消亡時,先執行封閉類的解構函式,然後再執行成員物件的解構函式。次序和建構函式的呼叫次序相反。
class CTyre { public: CTyre() { cout << "CTyre contructor" << endl; } ~CTyre() { cout << "CTyre destructor" << endl; } }; class CEngine { public: CEngine() { cout << "CEngine contructor" << endl; } ~CEngine() { cout << "CEngine destructor" << endl; } }; class CCar { private: CEngine engine; CTyre tyre; public: CCar( ) { cout << “CCar contructor” << endl; } ~CCar() { cout << "CCar destructor" << endl; } }; int main(){ CCar car; return 0; } 輸出結果: CEngine contructor CTyre contructor CCar contructor CCar destructor CTyre destructor CEngine destructor
3)封閉類的複製建構函式
封閉類的物件,如果是用預設複製建構函式初始化的,那麼它裡面包含的成員物件, 也會用複製建構函式初始化。
class A { public: A() { cout << "default" << endl; } A(A & a) { cout << "copy" << endl;} }; class B { A a; }; int main() { B b1,b2(b1); return 0; }