1 / 47

第 十二 章

第 十二 章. 類別 (Class). 大綱. 物件導向程式設計的特性 何謂類別?功用何在? 類別定義 類別與物件 成員函數 建構子與解構子 共享成員函數 共享資料成員 —static 之使用 物件指標之使用 類別與結構. 類別 (Class). 由 C 進展入 C++ 之最重要觀念為由 結構化程式 ( Structure programming ) 擴充到 物件導向程式設計 ( Object-oriented programming, OOP ) 。 類別 ( Class ) 則是物件導向程式設計的基礎,也是了解物件導向程式設計特性的最重要型態 :

lilian
Download Presentation

第 十二 章

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 第十二章 類別(Class)

  2. 大綱 • 物件導向程式設計的特性 • 何謂類別?功用何在? • 類別定義 • 類別與物件 • 成員函數 • 建構子與解構子 • 共享成員函數 • 共享資料成員—static之使用 • 物件指標之使用 • 類別與結構

  3. 類別(Class) • 由C進展入C++之最重要觀念為由結構化程式(Structure programming)擴充到物件導向程式設計(Object-oriented programming,OOP) 。 • 類別(Class)則是物件導向程式設計的基礎,也是了解物件導向程式設計特性的最重要型態: • 封裝(Encapsulation) 、多型(Polymorphism) • 繼承(Inheritance)、多型應用之重載(Overloading)、 • 繼承應用之衍生類別(Derived class) 。 • 樣版庫(Standard Template Library,STL)、 • 視覺化元件庫(Visual Component Liarary,VCL) 。

  4. 12-1物件導向程式設計的特性 • 就程式設計而言,有兩種方式: • 1.以設計的程式來影響資料(Around the code),這是結構化程式設計的重點,任何資料的改變都需要程式,程式與資料分開。 • 2. 以資料來影響程式(Around the data),即資料控制著程式碼的存取(data controlling access to code),這是物件導向程式設計的特色,唯有你所定義的資料與程式(函數)才可以對資料作存取。

  5. 12-1物件導向程式設計的特性 • 物件導向程式設計的三個特性: • 封裝(Encapsulation) • 多型(Polymorphism) • 繼承(Inheritance)

  6. 封裝(Encapsulation) • 封裝(Encapsulation): • 一種將資料(data)與程式碼(code)整合在一起的一種機制(mechanism) ,以確保安全與避免來自外面的誤用。 • 在物件內之資料、程式碼或兩者可以是私有的(private)或公用的(public) 。 • 私有的資料或程式碼只有物件內其他部分有存取權,物件外的程式是無法取得或看到的。 • 若資料或程式碼為公用的,則物件內與物件外的程式都可以存取;原則上公用的為提供一個良好介面以控制存取私有的資料或程式碼。

  7. 多型(Polymorphism) • 所謂多型在物件程式設計上就是「一種介面有多種方法(one interface, multiple methods) 」之意,簡單的說就是以單一介面來控制存取一般類別資料的行為。 • 在物件導向程式設計上有其應用,如你要處理三種不同型態之絕對值, 整數(int), 浮點數(float), 倍精度(double),因多型的關係可定義單一介面函數absolute( ),就可使用該三種方法,在程式內須對同一名稱定義不同的方法,編譯器就會自動去判斷在何種情況下使用何種方法。 • 多型可減少程式的複雜度,整合許多相類似或相同的功能附以統一的介面(即函數名稱), 以完成相異的工作。如:函數重載與運算子重載。

  8. 繼承(Inheritance) • 所謂繼承是一種承襲的過程,亦即一個物件除了可具有另一物件的特性外,自己也有自己的特性,而自己也可讓另一物件所繼承;宛如家譜或分類之分類,形成一階層式之架構。 • 如紅蘋果是蘋果之一類,蘋果又是水果之一類,水果又是食物之一類等,若不經過分類,將對每一種物體作明確的特性說明;經過分類後,只要簡單描述該物體是屬於何類及其特性即可。

  9. 12-2 何謂類別? • 在C++內若要建立一物件就必須建立類別,該類別須以保留字「class」起頭,類別就像結構,它是C++最重要的一種特性,不但包含有結構的特性,即是不同資料型態的集合外,又包含有如何處理該資料的函數,是資料與程式碼結合在一起的資料結構。 • 類別元素稱為成員(member)而不叫欄位(field),其成員分成員變數(member variables)或資料成員 (data member)與成員函數(member function),只有成員函數具有存取各個元素的權利。

  10. 12-2 何謂類別? • 類別內對各元素劃分成三大部分: • 一為私有部份(private) • 二為公用部份(public) • 三為保護部份(protected)

  11. 12-3 類別定義 • classkey classname • { member list • } • classkey:類別種類可為class,struct或union。 • classname:類別名稱。 • member list:類別內之資料成員與成員函數。

  12. 類別一般格式 class classname //類別名稱 { private: //存取指定子後需有冒號「:」 data and functions//私有資料與函數 public: data and functions //公用資料與函數 protected: data and functions //保護資料與函數 } object_name_list; //物件=類別變數

  13. 存取指定子 • 存取指定子(access-specifier): • private (私有的) • public(公用的) • protected(保護的) • 擺放位置可循環,但習慣上都把相同部份之資料與函數放在一起,若該資料與函數沒有指定則內定為private。 • object name list為該類別變數,稱為物件(object),類別定義完後可立即指定物件,或在程式內再宣告。

  14. 函數介面定義 • 函數介面定義 • 1.內部定義 • 將函數主體直接寫在類別內。 • 2.外部定義 • 將函數主體寫在類別外。

  15. 類別定義範例(介面內部定義) • class date //類別名稱 • { private: //私有 • unsigned int year, month, day; • public: //公有 • void set_date (int _year, int _month, int _day) • { year = _year; //函數主體,將外部傳入之資料設定給資料成員。 • month = _month; • day = _day; } • void list_date( ) //將資料輸出 • { cout << "year=" << year<< endl • << "month=" << month << endl • << "day=" << day << endl; } • } birth; //類別定義

  16. 12-4 類別與物件 • 類別經定義後就產生一個新的資料型態。 • 宣告為該資料型態之變數稱為類別變數在物件導向程式內稱為物件(Object) 。 • 如前面所定義之類別: • date mybirth, boybirth; • mybirth, boybirth為date類別名稱之類別變數,稱為物件(object)。 • 物件(object),跟一般的變數不同,包含了實作部份(介面),資料也有相當的隱密性,有動作、有行為、有方法,同類別而不同的物件可以共享這些方法,但其資料是不同的。

  17. 一般物件之使用 • 類別是一種邏輯上的定義,真正實作就有賴物件。 • 物件是類別的實例(instance),也只有物件才能使編譯器將記憶體分配給它。 • 物件在實作時需依類別定義之介面部分即public內之成員函數或資料成員來運作。 • 成員函數是一種函數,也是類別內的元素,所以物件在存取函數時是以點運算子「.」(dot operator)為之。

  18. 物件之範例(date) • date mybirth; • mybirth.set_data(1965, 12, 5); //設定資料 • mybirth.list_data( ); //輸出資料 • 執行結果為 • year=1952 • month=12 • day=5

  19. 例題: 設定出生日期資料後,將其輸出。 void list_date( ) //將資料輸出 { cout << "year=" << year << endl << "month=" << month << endl << "day=" << day << endl; } }; void main( ) { date mybirth; //物件宣告 mybirth.set_date(1970,12,5); mybirth.list_date( );  } #include <iostream> //cout using namespace std; class date //類別名稱 {private: //私有 unsigned int year, month, day; public: //公有 void set_date(int _year, int _month, int _day) //自外界取得資料 { year = _year; month = _month; day = _day; } 執行結果如下: year=1970 month=12 day=5

  20. 例題:定義圓資料之類別,輸入半徑資料後,將面積輸出。例題:定義圓資料之類別,輸入半徑資料後,將面積輸出。 float list_area( ) //公有成員函數 { return area; } }; void main( ) { Area circle; //物件宣告 circle.get_radius( ); //呼叫成員函數 cout <<"面積="<< circle.list_area( );  } #include <iostream> //cout using namespace std; classArea {private: int radius; //私有資料成員 float area; public: void get_radius( ) //公有成員函數 { cout << "半徑="; cin >> radius; area = 3.14159 * radius * radius; } 執行結果: 半徑=10 面積=314.159

  21. 例題:輸入資料到陣列求和與平均後輸出。 void list_data( ) { cout <<"總分="<< total << endl; cout <<"平均="<< ave << endl; } void main( ) { Total_Ave array; //物件宣告 array.get_array( ); //呼叫成員函數 array.list_data( ); //輸出資料 } #include <iostream> using namespace std; class Total_Ave { private: int a[5], total; float ave; public: void get_array( ) { total=0; cout << "輸入5個資料="; for (int n=0; n<5; n++) { cin >> a[n]; total += a[n]; } ave = total /5.0; }

  22. 例題:輸入資料到陣列求和與平均後輸出最大值,最小值。例題:輸入資料到陣列求和與平均後輸出最大值,最小值。 int get_max( ){ int max=a[0]; for (int i=1;i<5;i++) if (a[i] > max) max = a[i]; return max;} int get_min( ){ int min=a[0]; for (int i=1;i<5;i++) if (a[i] < min) min = a[i]; return min;} }; void main( ) { Total_Ave array; //物件宣告 array.get_array( ); //呼叫成員函數 cout <<"最大值="<<array.get_max( )<<endl; cout <<"最小值="<< array.get_min( )<<endl; } #include <iostream> using namespace std; class Total_Ave { private: int a[5], total; float ave; public: void get_array( ) { total=0; cout << "輸入5個資料="; for (int n=0; n<5; n++) { cin >> a[n]; total += a[n]; } ave = total /5.0; }

  23. 12-5 成員函數 • 成員函數是類別對外的介面,如何使類別內元素發揮其功能以及看起來清楚易懂,就需要將成員函數之主體定義在外面,這就需要藉助範圍運算子「::」,而對較少程式碼如一二列者,能在編譯時放在類別內以加快執行速度,這就需要藉助inline。

  24. 範圍運算子「:: 」 • 將成員函數程式碼定義在類別外面,類別定義內只要作函數原型宣告就可。 • 格式如下: • 資料型態 類別名稱 :: 函數名稱(參數) • { • 函數主體; • }

  25. 範圍運算子「:: 」 • 一. 類別名稱必須指名,且須放在函數名稱之前。 • 二. 在類別名稱與函數名稱之間需擺上範圍運算子「::」 • 如: • void AA :: get_data( ) {函數主體} • void BB :: get_data( ) {函數主體} • get_data( )在AA與BB類別名稱都相同,但因隸屬於不同的類別而有差異,這是範圍運算子之功用。

  26. 例題:輸入資料到陣列求和與平均後輸出。(將成員函數定義在類別外)例題:輸入資料到陣列求和與平均後輸出。(將成員函數定義在類別外) void Total_Ave :: list_data( ) { cout <<"總分="<< total << endl; cout <<"平均="<< ave << endl; } void main( ) { Total_Ave array; //物件宣告  array.get_array( ); //呼叫成員函數 array.list_data( ); //輸出資料 } #include <iostream> //cout using namespace std; class Total_Ave { private: int a[5], total; float ave; public: void get_array( ); //函數原型宣告 void list_data( ); //輸出 }; void Total_Ave :: get_array( ) { total=0; cout << "輸入5個資料="; for (int n=0; n<5; n++) { cin >> a[n]; total += a[n]; } ave = total / 5.0; }

  27. 例題:輸入資料到陣列求和,最大值,最小值與平均後輸出。(將成員函數定義在類別外)(1)例題:輸入資料到陣列求和,最大值,最小值與平均後輸出。(將成員函數定義在類別外)(1) int Total_Ave :: get_max( ) { int max=a[0]; for (int i=1;i<5;i++) if (a[i] > max) max = a[i]; return max;} int Total_Ave::get_min( ) { int min=a[0]; for (int i=1;i<5;i++) if (a[i]<min) min = a[i]; return min;} void main( ) { Total_Ave array; //物件宣告  array.get_array( ); //呼叫成員函數 cout <<"最大值="<< array.get_max( ) << endl; cout <<"最小值="<< array.get_min( ) << endl; } #include <iostream> //cout using namespace std; class Total_Ave { private: int a[5], total; float ave; public: void get_array( ); //函數原型宣告 int get_max( ); int get_min( ); }; void Total_Ave :: get_array( ) { total=0; cout << "輸入5個資料="; for (int n=0; n<5; n++) { cin >> a[n]; total += a[n]; } ave = total / 5.0; }

  28. inline函數 • 編譯時會將類別外定義成員函式(簡短程式碼)放入類別定義內的成員函數稱為inline函數。 • 提高執行效率。 • 格式: • inline 資料型態 類別名稱 :: 函數名稱(參數) • { 函數主體; } • 類別內之原型宣告為: • inline 資料型態 函數名稱(參數);

  29. 例題:將Area之list_area定義為inline完整程式。 inline float Area :: list_area( ) { return area; } void main( ) { Area circle; //物件宣告   circle.get_radius( ); cout << "面積= " << circle.list_area( ); } #include <iostream> //cout using namespace std; class Area { private: int radius; float area; public: void get_radius( ); inline float list_area( ); }; void Area :: get_radius( ) { cout << “半徑=”; cin >> radius; area = 3.14159 * radius * radius;}

  30. 成員函數重載 • 函數重載(Function overloading)也可使用在成員函數內,只要物件對這些參數給與不同的數目(參數)或不同的資料型態,就可使編譯器判斷何種函數可以啟動。 • void get_radius( ); //由使用者者輸入 • void get_radius(int ); //傳數值給函數 • void Area :: get_radius( ) //無參數 • { cout << “半徑=”; • cin >> radius; • area = 3.14159 * radius * radius; } • void Area :: get_radius(int _radius) //單一參數 • { radius = _radius; • area = 3.14159 * radius * radius; }

  31. 例題:成員函數重載之應用。 • void Area::get_radius(int _radius) • { radius = _radius; • area = 3.14159 * radius * radius;} • inline float Area::list_area() //inline函數 • { return area;} • void main(){ • Area circle; //物件宣告 • circle.get_radius(); //呼叫成員函數 • cout << "面積="<< circle.list_area(); • cout << endl; • circle.get_radius(20); //傳初值 • cout << "半徑20之面積=“ • << circle.list_area() << endl; • } • #include <iostream> • using namespace std; • class Area{ • private: • int radius; • float area; • public: • void get_radius(); //無參數 • void get_radius(int ); //單一參數 • inline float list_area(); //inline之原型 • }; • void Area::get_radius( ) • { cout << "半徑="; cin >> radius; • area = 3.14159 * radius * radius; • }

  32. 資料成員之初值 • 設定類別資料成員之初值有下面兩種: • 以成員函數來設定 • 只要物件一產生就使資料成員歸零或設定在某一數值

  33. 成員函數設定初值 • 將類別名稱Area內之資料成員radius及area設為0或某一初值,作法為: • void Area :: init( ) • { radius =0; • area=0;} • 或 • void Area :: init(int _radius, float _area) • { radius = _radius; • area = _area;} • 起始資料為(Area circle;) • circle.init( ); 或 circle.init(0, 0);

  34. 例題:物件初值之建立。 • void Area::init(int _radius,float _area){ • radius = _radius; • area=_area; } //設定某一資料 • inline float Area::list_area() //inline函數 • { return area;} • void main() • { Area circle; //物件宣告 • int _radius = 10; • float _area=PI*_radius*_radius; • circle.init(_radius,_area); • cout << "面積="<< circle.list_area(); • cout << endl; • circle.init(); //清除資料 • cout << "面積="<< circle.list_area() << endl; } • #include <iostream> using namespace std; • const float PI=3.14159; • class Area{ • private: • int radius; • float area; • public: • void init(); //無參數之啟始 • void init(int,float); //啟始某一初值 • inline float list_area(); //inline之原型 • }; • void Area::init(){ • radius=0; • area=0; } //清除資料

  35. 12-6 建構子與解構子 • 可設定初值之成員函數稱為建構子(Constructor),建構子也可以攜帶參數,所以也有重載之特性,有了建構子自然也有解構子(Destructor)。 • 建構子是一個特殊的成員函數,要定義建構子必需遵循下列兩個原則: • 建構子的名稱需與所屬類別名稱相同,才能告訴編譯器這是建構子。 • 建構子不能傳回資料型態,因為它是系統自行呼叫,自然就不必傳回任何資料。 • 建構子之定義可定義在類別內部也可定義在外部。

  36. 解構子 • 解構子(Destructor)就是將建構子所配置記憶體釋放出來。 • 要自行定義解構子應注意: • 解構子在一個類別只能有一個,名稱也需與類別名稱相同。 • 解構子必須在類別名稱前加上波浪型符號「~」。 • 解構子不可以有參數,也不可以傳回資料型態。 • 當物件消滅時會自動呼叫,請勿自行呼叫。

  37. 例題:以建構子完成初值之設定。 inline float Area::list_area(){ return area; } void main(){ Area circle; //物件宣告circle cout << "面積="<< circle.list_area()<<endl; } • #include <iostream> • using namespace std; • class Area{ • private: • int radius; • float area; • public: • Area(); //無參數建構子 • inline float list_area(); • }; • Area::Area(){ • radius=0; • area=0; • cout << “我是建構子”<<endl; • }//類別外宣告

  38. 建構子與解構子 • 如: • class App{ • private: • int data; • public: • App( ) { data=0; } //建構子 • ~App( ){ }; //解構子 • };

  39. 12-7 共享成員函數 • 當一個類別定義之後,可以建立許多物件(或稱類別變數),電腦會對這些物件隨資料型態大小分配靜態記憶體給它們,若連成員函數也要每個物件都要分配,將使記憶體資源恨快就會耗盡,實際上,不管同類別之物件有幾個,除各別擁有資料成員外,成員函數都只有一份,因不同的物件不會同時啟動同一成員函數,即使可能發生,作業系統也會有列隊(Queue)的管理,所以讓物件共享成員函數有下列優點: • 1. 減少記憶體分配 • 2. 加快執行速度

  40. 資料成員與成員函數之關係 物件A 物件B 物件C 資料 資料 資料 成員函數

  41. 12-8 共享資料成員—static之使用 • 成員函數對任何同類別之物件而言是只有一份,大家共同使用,資料成員在類別定義時也可宣告那些是共用的,如可宣告一資料成員來累計一共產生多少物件,或用來計數磁碟讀取的狀況,總之它是共享的,任何物件都可存取,此時就需static之幫忙 。

  42. 資料成員共享 • 資料成員共享應注意: • 在資料型態前加上保留字static。如: static int number; • 共享資料成員第一次執行除非有另設初值,否則數值為0,字串為空字串,如: number之初值為0。 • 必須在類別外定義該資料成員,其格式為: 資料型態 類別名稱 :: 資料成員=初值;

  43. 資料成員共享 • 如: • class Car • {static int count; //共享資料成員 • int id; • public: • Car( ); //沒有參數的建構子 • Int list_car( ); }; • int Car :: count; //靜態共享資料之定義,初值為0 • 等於 • int Car :: count = 0; 或 • int Car :: count = 10; //設定初值為10

  44. 例題:靜態資料成員之應用。 Car::Car(){ //建構子 ++count; //靜態資料加一 id = count; }//將結果設定給id void Car::list_car(){ cout<<“總數有”<<count <<“部,這是第”<<id<<“ 部\n”;} void main(){ Car A,B,C,D; //物件宣告 A.list_car(); B.list_car(); C.list_car(); D.list_car(); } • #include <iostream> • using namespace std; • class Car{ • private: • static int count; //靜態資料 • int id; • public: • Car(); //無參數之建構子 • void list_car(); • ~Car(){}; //解構子 • }; • int Car::count; //靜態宣告

  45. 12-9 物件指標之使用 • 一般物件在建立時是靜態的,所需之記憶體在編譯時就已經配置好,直到程式結束前仍保有該處之記憶體。 • 若用指標來使物件建立時才取得記憶體,用完後歸還,不但不會佔用空間也可達到資源回收之目的,使用指標建立物件稱物件指標(pointer of object) 。 • 指標之使用: • 以new配置記憶體。 • 以箭頭運算子「->」來取得各成員函數。 • 以delete釋放記憶體。

  46. 12-10 類別與結構 • 結構(struct)也是類別之一種,它是將資料集合成群。 • 類別(class)是將資料與函數封裝在一起。 • 兩者之使用方式相同,兩者唯一不同之處是,在預設的情況下類別之所有成員為私有的(private),而結構則為公有的(public)。

  47. 類別單元練習題 • 設計一個CBox類別,具有length,width與height三個整數的資料成員,並完成下列程式設計:(a)定義Volume( )函數,用來計算CBox物件的體積。(b)定義SurfaceArea( )函數,用來計算CBox物件的表面積。 • 設計一個Calculator類別,並完成下列程式設計: (a)定義add(a,b )函數,用來計算二數之和。 (b)定義sub(a,b )函數,用來計算二數之差。 (c)定義mul(a,b )函數,用來計算二數之積。 (d)定義div(a,b )函數,用來計算a / b。 • 設計一個加法類別Addition,並設計sum( )函數的多載,使其具有下面功能: (1) sum(int a, int b)。 (2)sum(double a, double b)。 (3) sum(int a, int b, int c)。 (4) sum(double a, double b, double c)。 • 設計一個Person類別,公用資料包含︰姓名,年齡,性別;私用資料包含:電話,身分證號碼。並設計一個建構函式,用來初始化各項資料。

More Related