类和对象提高
程序設計與算法(三)C++面向對象程序設計-郭煒 第三周
總結整理:
目錄:
- 1.this指針
- 2.靜態成員
- 3.成員對象和封閉類
- 4.友元 (friends)
- 5.常量成員函數
1.this指針
C++程序到C程序的翻譯
class CCar { public: int price; void SetPrice(int p);}; void CCar::SetPrice(int p){ price = p; } int main() { CCar car; car.SetPrice(20000); return 0; } ------------------------------------------------ struct CCar { int price; }; void SetPrice(struct CCar * this, int p) { this->price = p; } int main() { struct CCar car; SetPrice( & car, 20000); return 0; }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(); //c1被修改,把c1的值賦值給c2return 0; } //輸出 2,1 class A { int i;public: void Hello() { cout << "hello" << endl; } }; // void Hello(A * this ) { cout << "hello" << endl; } int main() { A * p = NULL; p->Hello(); // Hello(p); } // 輸出:hello-------------------------------------------- class A { int i;public: void Hello() { cout << i << "hello" << endl; } }; // void Hello(A * this )// { cout << this->i << "hello" << endl; } //this若為NULL,則出錯!! 找不到空指針指向的對象的i int main() { A * p = NULL; p->Hello(); // Hello(p); } // 輸出:hello靜態成員函數中不能使用 this 指針!
因為靜態成員函數并不具體作用與某個對象!
因此,靜態成員函數的真實的參數的個數,就是程序中寫出的參數個數!
類的非靜態成員函數,真實的參數比所寫的參數多1,多出來的是this指針
2.靜態成員
靜態成員:在定義前面加了static關鍵字的成員
class CRectangle { private: int w, h; static int nTotalArea; //靜態成員變量 static int nTotalNumber; public: CRectangle(int w_,int h_); ~CRectangle(); static void PrintTotal(); //靜態成員函數 };普通成員變量每個對象有各自的一份,而靜態成員變 量一共就一份,為所有對象共享。
同一個類的不同的對象,普通成員變量不一樣。而靜態成員變量則一樣
sizeof 運算符不會計算靜態成員變量
普通成員函數必須具體作用于某個對象,而靜態成員 函數并不具體作用于某個對象。不在一個對象內部,而是放在所有對象外部,為所有對象所共享
因此靜態成員不需要通過對象就能訪問。
靜態成員變量本質上是全局變量,哪怕一個對象都不存在,類 的靜態成員變量也存在。
靜態成員函數本質上是全局函數。
設置靜態成員這種機制的目的是將和某些類緊密相關的全局變量和函數寫到類里面,看上去像一個整體,易于維護和理解。別的類沒法訪問這個變量
如何訪問靜態成員
1) 類名::成員名
CRectangle::PrintTotal();
2) 對象名.成員名
CRectangle r; r.PrintTotal();
3) 指針->成員名
CRectangle * p = &r; p->PrintTotal(); 并不只屬于到p所指向的那個對象
4) 引用.成員名
CRectangle & ref = r; int n = ref.nTotalNumber
靜態成員示例
考慮一個需要隨時知道矩形總數和總面積的圖形 處理程序
可以用全局變量來記錄總數和總面積
用靜態成員將這兩個變量封裝進類中,就更容易 理解和維護
此CRectangle類寫法, 有何缺陷?
在使用CRectangle類時,有時會調用復制構造函數 生成臨時的隱藏的CRectangle對象
調用一個以CRectangle類對象作為參數的函數時,
調用一個以CRectangle類對象作為返回值的函數時
臨時對象在消亡時會調用析構函數,減少nTotalNumber 和 nTotalArea的值,可是這些臨時對象在生成時卻沒有增加 nTotalNumber 和 nTotalArea的值,因此最終可能數量小于應有的。
解決辦法:為CRectangle類寫一個復制構造函數
在靜態成員函數中,不能訪問非靜態成員變量, 也不能調用非靜態成員函數。非靜態成員函數可能需要調用非靜態成員變量,那不能確定是哪一個非靜態成員變量,因此不能
void CRectangle::PrintTotal() { cout << w << "," << nTotalNumber << "," << nTotalArea << endl; //wrong} CRetangle::PrintTotal(); //解釋不通,w 到底是屬于那個對象的?3.成員對象和封閉類
有成員對象的類叫 封閉(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 的初始化沒問題,用 默認構造函數即可。
任何生成封閉類對象的語句,都要讓編譯器明白,對象中的成員對象,是如何 初始化的。
具體的做法就是:通過封閉類的構造函數的初始化列表。
成員對象初始化列表中的參數可以是任意復雜的表達式,可以包括函數,變量 ,只要表達式中的函數或變量有定義就行。
封閉類構造函數和析構函數的執行順序
封閉類對象生成時,先執行所有對象成員的構造函數,然后才執 行封閉類的構造函數。
對象成員的構造函數調用次序和對象成員在類中的說明次序一致 ,與它們在成員初始化列表中出現的次序無關。
當封閉類的對象消亡時,先執行封閉類的析構函數,然后再執行 成員對象的析構函數。次序和構造函數的調用次序相反。
封閉類實例
#include<iostream> using namespace std;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封閉類的復制構造函數
封閉類的對象,如果是用默認復制構造函數初始化的,那么它里面包含的成員對象, 也會用復制構造函數初始化
4.友元 (friends)
友元分為友元函數和友元類兩種
1) 友元函數: 一個類的友元函數可以訪問該類的私有成員.
class CCar ; //提前聲明 CCar類,以便后面的CDriver類使用 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( cars[i].price > tmpMax) tmpMax = cars[i].price; return tmpMax; } int main() { return 0; }可以將一個類的成員函數(包括構造、析構函數)說明為另一個類的友元。
class B { public: void function(); };class A { friend void B::function(); };2)友元類: 如果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的友元類, //故此處可以訪問其私有成員 }; int main(){ return 0; }友元類之間的關系不能傳遞,不能繼承
5.常量成員函數
如果不希望某個對象的值被改變,則定義該對象的時候可以在前面加 const關鍵字。
class Sample { private : int value; public: Sample() { } void SetValue() { } }; const Sample Obj; // 常量對象 Obj.SetValue (); //錯誤 。常量對象只能使用構造函數、 //析構函數和有 const 說明的函數(常量方法在類的成員函數說明后面可以加const關鍵字,則該成員函數成為常量 成員函數。
常量成員函數內部不能改變屬性的值,也不能調用非常量成員函數
在定義常量成員函數和聲明常量成員函數時都應該使用const 關鍵字。
class Sample { private : int value; public: void PrintValue() const; }; void Sample::PrintValue() const { //此處不使用const會 //導致編譯出錯 cout << value; } void Print(const Sample & o) { o.PrintValue(); } //若 PrintValue非const則編譯錯如果一個成員函數中沒有調用非常量成員函數 ,也沒有修改成員變量的值,那么,最好將其 寫成常量成員函數
常量成員函數的重載
兩個函數,名字和參數表都一樣,但是一個是const,一個不是,算重載。
#include <iostream> using namespace std;class CTest { private : int n; public: CTest() { n = 1 ; } int GetValue() const { return n ; } int GetValue() { return 2 * n ; } };int main() {const CTest objTest1;CTest objTest2; cout << objTest1.GetValue() << "," << objTest2.GetValue() ; return 0; } //1,2mutable成員變量
可以在const成員函數中修改的成員變量
總結
- 上一篇: html表格支持响应,将表格响应转换为H
- 下一篇: sql相同顺序法和一次封锁法_数学专题