C++面向對象程序設計(三)——3.類和對象提高
C++面向對象程序設計(三)——3.類和對象提高
本文是中國大學MOOC,北京大學程序設計與算法(三)C++面向對象程序設計第三周筆記。本課程學習的github倉庫歡迎Fork
本文目錄
C++面向對象程序設計(三)——3.類和對象提高
一 this 指針
C++ 到 C程序的翻譯
二 靜態成員
基本概念
如何訪問靜態成員
1.類型::成員名
2.對象名.成員名
3.指針->成員名
4.引用.成員名
三 成員對象和封閉類
基本概念
封閉類構造函數和析構函數的執行順序
四 友元
友元函數
友元類
五 常量成員函數
六 mutable成員變量
一 this 指針
C++ 到 C程序的翻譯
//C++ class CCar{ public: int price; void SetPtice(int p); }; void CCar::SetPrice( int p ){ price = p; } int main(){ CCar car; car.SetPrice( 20000 ); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//C struct CCar{ int price; }; void SetPrice( struct CCar * this, int p) { this -> price = p; } int main(){ struct CCar car; SetPrice( & car, 20000 ); return 0; }
1
2
3
4
5
6
7
8
9
10
11
12
13
對比一下這兩個程序,我們可以看到:
每個類的非靜態成員函數中都隱含包含一個this指針,類型為當前類類型的指針類型
this 作用就是指向成員函數所作用的對象。在非靜態成員函數中可以直接使用this來代表指向該函數作用的對象的指針。
我們可以看一個案例:
class Complex{ public: double real,imag; void Print(){ cout << real << "," << imag; } Complex( double r, double i ):real( r ),imag( i ) {} Complex AddOne(){ this -> real ++; //等價于 real++ this -> Print(); //等于于 Print return * this; } }; int main(){ Complex c1(1,1),c2(0,0); c2 = c1.AddOne(); return 0; }//輸出 2,1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
需要注意的是:
靜態成員函數中不能使用this指針,因為靜態成員函數并不具體作用于某個對象。所以,靜態成員函數的真實參數的個數,就是程序中寫出的參數個數。
然而,類的非靜態成員函數,真實的參數比所寫的參數多1,多的這個就是this指針。
二 靜態成員
基本概念
在定義前面加了static關鍵字的成員
class CRectangle { private: int w, h; static int nTotalArea; //靜態成員變量 static int nTotalNumber; public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); //靜態成員函數 };
1
2
3
4
5
6
7
8
9
10
11
靜態成員變量為所有對象共享,且sizeof運算符不會計算靜態成員變量
class CMyclass{ int n; static int s; }; //sizeof(CMyclass) 等于 4
1
2
3
4
5
普通成員變量每個對象有各自的一份,而靜態成員變量一共就一份,為所有對象共享
普通成員函數必須具體作用于某個對象,而靜態成員函數并不具體作用于某個對象。所以實際上靜態成員不需要通過對象就能訪問。
如何訪問靜態成員
CRectangle::PrintTotal();
1
CRectangle r; r.PrintTotal();
1
2
CRectangle * p = &r; p -> PrintTotal();
1
2
CRectangle & ref = r; int n = ref.nTotalNumber;
1
2
靜態成員變量本質上是全局變量,哪怕一個對象都不存在,類的靜態成員變量也存在
靜態成員函數本質上是全局函數,主要目的是將和某些類緊密相關的全局變量和函數寫到類里面,看上去想一個整體,易于維護和理解
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_; nTotalNmuber ++; nTotalArea += w * h; } CRectangle::~CRectangle() { nTotalNumber --; nTotalArea -= w * h; } void CRectangle::PrintTotal() { cout << nTotalNumber << "," < 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 要注意的是:在靜態成員函數中,不能訪問非靜態成員變量,也不能調用非靜態成員函數 必須在類外面對靜態函數變量進行說明或初始化 void CRectangle::PrintTotal() { cout << w << "," << nTotalNumber << "," << nTotalArea << endl; //error } CRectangle::PrintTotal(); //解釋不通,w到底屬于哪個對象? 1 2 3 4 5 三 成員對象和封閉類 基本概念 有成員對象的類叫封閉類 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 yre; 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; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 上面的例子中,如果CCar類不定義構造函數,那么下面的語句編譯會出錯 CCar car; 因為編譯器不明白car.type,該如何初始化。car.engine的初始化沒問題,用默認構造函數就可以了。 任何生成封閉類對象的語句,都要使得編譯器明白,對象中的成員對象,是如何初始化的。完成這一任務的方法是通過封閉類的構造函數的初始化列表,成員對象初始化列表中的參數可以是任意復雜的表達式,可以包括函數,變量,只要表達式中的函數或變量有定義就行。 封閉類構造函數和析構函數的執行順序 封閉類對象生成時,先所有成員對象的構造函數,然后才執行封閉類的構造函數 對象成員的構造函數調用次序和對象成員在類中的說明次序一致,與成員初始化列表次序無關 封閉類對象消亡時,先執行封閉類析構函數,再執行成員對象的析構函數。次序和構造函數的調用次序相反 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 constructor" << endl;} ~CCar(){ cout << "CCar destructor" << endl;} } int main(){ CCar car; return 0; } //輸出結果 //CEngine contructor //Ctyre contructor //CCar constructor //CCar destructor //Ctyre destructor //CEngine destructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 封閉類的對象如果用默認復制構造函數初始化,那么它包含的成員對象也會用復制構造函數初始化 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; } //輸出: //default //Copy //b2.a是用類A的復制構造函數初始化的。 //調用復制構造函數的實參是b1.a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 四 友元 友元函數 一個類的友元函數可以訪問該類的私有成員 class CCar; class CDriver{ public: void ModifyCar(CCar * pCar); }; class CCar { private: int price; friend int MostExpensiveCar( CCar cars[], int total ); //聲明友元 friend void CDriver::ModifyCar( CCar * pCar ); //聲明友元 } void CDriver::ModifyCar( CCar * pCar ) { pCar->price += 1000 ; //改裝后變貴了 } int MostExpensiveCar( CCar cars[],int total ) //最貴汽車價格 { int tmpMax = -1; for( int i = 0; i < total ; ++ i ) { if(car[i].price > tmpMax) tmpMax = cars[i].price; } return tmpMax; } int main(){ return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 當然我們還可以把一個類的成員函數(包括析構,構造等)說明為另一個類的友元 class B{ public: void function(); }; class A{ friend void B::function(); } 1 2 3 4 5 6 7 8 友元類 如果A是B的友元類,那么A的成員函數可以訪問B的私有成員 class CCar{ private: int price; friend class CDriver; //聲明CDriver為友元類 }; class CDriver{ public: CCar myCar; void ModifyCar(){ //改裝汽車 myCar.price += 1000; //CDriver是CCar的友元類,所以可以訪問CCar的私有成員 } }; int main(){ return 0; } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 友元類之間的關系不能傳遞,不能繼承 五 常量成員函數 如果不希望某個對象的值被改變,那么可以在對象前加const關鍵字 class Sample{ private: int value; public: Sample(){} void SetValue(){} }; const Sample Obj; //常量對象 Obj.SetValue (); //錯誤,常量對象只能使用構造函數,析構函數,有const說明的函數 1 2 3 4 5 6 7 8 9 10 類的成員函數說明后面可以加const關鍵字,該成員函數成為常量成員函數 常量成員函數內部不能改變屬性的值,也不能調用非常量成員函數 在定義和聲明成員函數時都應該用const關鍵字 如果一個成員函數中沒有調用非常量成員函數,也沒有修改成員函數變量的值,那么最好將其寫成常量成員函數 如果兩個函數,名字參數表都一樣,但是一個有const,一個沒有,算重載 六 mutable成員變量 可以在const成員函數中修改的成員變量 class CTest{ public: bool GetData() const { m_n1++; return m_b2; } private: mutale int m_n1; bool m_b2; }; 1 2 3 4 5 6 7 8 9 10 11 C++ 面向對象編程
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。