460 likes | 522 Views
繼承 ( Inheritance). 前言. 利用繼承的觀念, 可擴充舊的程式 為目前所須要的狀態 繼承舊的資料型態 定義出新的資料型態 新資料型態同時擁有 舊 的資料型態 method & data 新 的 method & data 新資料型態亦 可繼續被繼承. 基礎類別與衍生類別. 在物件導向程設中,被繼承的類別稱為 基礎類別 ( base class) 由繼承關係所定義出來的類別稱為 衍生類別 ( derived class). 基礎類別. 衍生類別. 馬上看看繼承的範例. 直接使用繼承而來的資料. 基礎類別下的資料隱藏.
E N D
前言 • 利用繼承的觀念,可擴充舊的程式為目前所須要的狀態 • 繼承舊的資料型態定義出新的資料型態 • 新資料型態同時擁有 • 舊的資料型態 method & data • 新的method & data • 新資料型態亦可繼續被繼承
基礎類別與衍生類別 • 在物件導向程設中,被繼承的類別稱為基礎類別 (base class) • 由繼承關係所定義出來的類別稱為衍生類別 (derived class) 基礎類別 衍生類別 馬上看看繼承的範例
直接使用繼承而來的資料 基礎類別下的資料隱藏 如果已經寫好一個Based類別 class Based { private: int Mydata; protected: int Mydata2; public: Based() { Mydata=1234; Mydata2=5678; } }; 如何有效的重複使用過去的程式 我們使用繼承 class Derived : publicBased { public: Derived() { Mydata2++; } };
private 封裝的資料 基礎類別下的資料隱藏 • private中的資料成員與成員函數 • 只可被該類別的成員函數使用 (不具備繼承的性質) • protected中的資料成員與成員函數 • 可被自己使用 • 亦可被衍生類別使用. (具備繼承的性質) • 別人不能使用 (資料隱藏) example example 驗證看看 NEXT
private 封裝的資料 add(){ } Add 可以存取自己範圍內 的 private Data Private 成員存取範例 class Complex{ private: double Real; double Img; public: Complex(...); void add( int ConstValue){ Real+=ConstValue; Img+=ConstValue; } }; BACK
class 動物{ 吃(); 喝(); } 基本的行為寫在 基礎類別 特別物種的行為寫在 衍生類別 class 人類{ 玩( ); 樂( ); } class 貓{ 洗臉( ); } 使用繼承的時機 (1/2) • 首先要把工作分類 • General Case • Special Case (基礎類別) (衍生類別)
使用繼承的時機 (2/2) • 情況: 學生成績 • 基礎類別只記錄共同科目的成積, • 對於某科系而言,還有專業科目 • 怎麼辦 ? • 直接修改基礎類別 • 或利用繼承的方法,設計衍生類別 • 不必動到基礎類別的任何程式碼 • 資源可以重複利用
資工系::衍生類別 class Computer:public Com { public: int AI; Computer() { AI=0; } }; 繼承的範例 共同科目::基礎類別 已經寫好處理共同科目程式 class Com { protected: int Chinese; int English; public : Com() { Chinese=0; English=0; } void SetGrade(int Chinese,int English) { this->Chinese=Chinese; this->English=English; } }; 利用繼承,加入人工智慧科目 加入新的科目
公用的基礎類別 繼承的語法 定義衍生類別的格式如下: class 衍生類別名稱:public基礎類別 { … }; • 繼承的方式 • public指明了基礎類別=>公用的基礎類別 • 亦可用 private指明基礎類別 為一私用基礎類別
私用的基礎類別 衍生類別的定義方法 • 當使用者沒有指明 private 或 public時: • 則內定為 private, 因此類別定義可以寫成 class Chemistry: ComFinal { … }; class Chemistry: private ComFinal { … }; 或
繼承的方式公用基礎類別VS 私用基礎類別 • public基礎類別: • 到自己的public區與protected區 • private 基礎類別: • 到自己的private區 基礎類別 衍生類別 private 區 private 區 protected 區 protected 區 基礎類別 衍生類別 public 區 public 區 private 區 private 區 Public方式繼承 protected 區 protected 區 public 區 public 區 看看圖示 Private方式繼承
繼承的方式公用基礎類別VS 私用基礎類別 • 不論是public或private基礎類別 • 其private區中的所有資料成員與成員函式都不可被繼承. 繼承的方式 資料隱藏與繼承的關係 基礎類別public基礎類別 private基礎類別 public public private protected protected private private 無法繼承無法繼承 Based Class 資料成員的存取屬性 該資料成員在Derived 中的存取屬性 該資料成員在Derived 中的存取屬性
繼承的方式公用基礎類別VS 私用基礎類別 • 例如 衍生類別B以 private的方式繼承了基礎類別A,則A的所有可繼承的成員都變成了衍生類別B的private成員 • 這使得基礎類別失去了 再被繼承的性質 基礎類別 A private 衍生類別 B public or private 衍生類別 C 無法由 A 繼承 任何成員
繼承的方式公用基礎類別VS 私用基礎類別 • 如果使用者希望一個基礎類別可以不斷的被衍生類別所繼承下去,則必須都設定為public 基礎類別 基礎類別 A public 衍生類別 B public 繼承A中非private 成員 衍生類別 C …. 衍生類別 Z
衍生類別之資料成員與成員函式的定義 • 在繼承關係下 • 只有public區與protected的資料成員與成員函式,可以被繼承. • 衍生類別只須定義新加入的成員, • 無法繼承的成員 • private區的所有成員 • Based 的建構子 class Base int weight; class Cat int sleep();
存在! 但不能直接存取. • 你可以用公共介面存取私用成員 • 宣告為 private 的成員,不能被繼承. 表示那些成員不存在於衍生物件中 ???
Base class 建構子 ( 1 ) 建構子 ( 2 ) 建構子 ( 3 ) 意思是說 想要使用 建構子(2)來initial 基礎類別的資料成員, 怎麼辦?? ? Derived class 基礎類別 有許多建構自己的方法 好了, 現在我要用 指定的建構子建構基礎類別!
class Based{ public: Based() {} }; class Derived:public Based{ public: Derived(){ } }; 使用基礎類別的預設建構子 指定要執行的 基礎類別的建構子 衍生類別的建構子 注意:用冒號 在繼承下衍生類別之Constructor的設計 • 呼叫基礎類別的建構子 • 忽略不寫. [預設建構子] 2. 由衍生類別的初值列中呼叫 [ 指定建構子] Chemistry:: Chemistry( …) : ComFinal( …)
在衍生類別中呼叫 Base class的constructor 範例 class Base { public: int basedata; Base(int data) { basedata=data; } }; class Derived:publicBase { public: int Derdata; Derived(int data): Base(data), Derdata(123){ } }; 建構子的初值列 基礎類別建構子 初值列設定初值
範例 class Base{ protected: int a,b,c; public: Base(int x=0,int y=0, int z=0); }; 衍生類別之constructor的進一步討論---Constructor 的呼叫順序 • Ok! 哪一個建構子先被執行? • 先執行基礎類別constructor • 而解構子,則相反
衍生類別之constructor的進一步討論---Constructor 的呼叫順序 Derived.h Class Derived: public Base { protected: int d,e; public: Derived(int x,int y,int z,int o,int p); }; Derived::Derived(int x,int y,int z,int o,int p): Base(x,y,z) { … } Derived.cpp 2 1
衍生類別之constructor的進一步討論--忽略呼叫基礎類別constructor的情況衍生類別之constructor的進一步討論--忽略呼叫基礎類別constructor的情況 • 若忽略在衍生類別的成員初設串列 • 衍生類別會呼叫基礎類別的 預設建構子 class point{ protected: int x,y; public: point(int a, int b=0); point() {x=0 ; y=0;} } 沒有參數的建構子 就是預設建構子
line 繼承 point class line: public point { protected: int len; pulbic: line(int a,int b, int len); line(int len); line(); }; line::line(int a,int b, int l) : point(a,b) // line 的constructor { len=l; } line:: line(int l) { len=l; } Line 類別有兩個建構子 指定呼叫兩個參數的建構子 1 2 沒指定,則呼叫預設的建構子
如何使用繼承而來的成員 class Errormessage{ protected: char ** mes; int hmes; public:char * get_error_message(); }; 基礎類別 使用ShowError呼叫 class ShowError: public Errormessage{ public: int line; void show_error(const char * prompt) { } }; 衍生類別 ShowError Obj; Obj.get_error_message(); Obj.hmes++; 可直接呼叫 get_error_message(); 或 使用 hmes 變數 外界不能直接存取 Protected成員
內部自己的method 公用介面 如何使用繼承而來的成員 • Show_error 可以直接使用由基礎類別繼承而來的成員函數 • get_error_message() • 亦可使用物件來使用繼承而來的成員函數 例如 Showerror Error(error,0,5); Error.get_error_message(); • 即藉由衍生類別 Showerror的物件可以 • 使用自身定義的成員函數,也可用繼承而來的成員函數
好了, 現在我要更改 繼承而來的物件行為. 如何做??? Overriding(覆寫) member
class Derived:public Based{ public: int data; void Run() { 做其他的運算 Ok(); } }; Overriding(覆寫) member • 衍生類別與基礎類別成員的名稱 • 相同 修改Run(),其他的不變 class Based{ public: int data; void Run() { 原來的版本 OK(); } void Ok() { … } };
不執行Based 的Run() 3 2 1 class Based{ public: int data; void Run() { 原來的版本 OK(); } void Ok() { … } }; 基礎類別 class Derived:public Based{ public: int data; void Run() { 做其他的運算 Ok(); } }; 衍生類別 DerObj-> Run(); 繼續執行
Overriding(覆寫) member • 被隱藏不代表消失,它們仍舊是衍生類別的一部份,透過明確的範圍指定,依舊可以存取到這些成員 • Dclass.Base:: showID() 明確指定呼叫 Base 的 function 看一下範例
使用範例 class Errormessage { … void show(); … }; class showerror: public Errormessage{ … void show(const char * prompt); … }; void showerror::show(const char *prompt) { … Errormessage::show(); … } 基礎類別 衍生類別 明確指定呼叫 Errormessage的 show()
Overriding Vs Overloading function • Overrinding function 乃指在衍生類別中重新定義基礎類別中已存在的函數名稱 • 只要 name相同,則基礎類別就會被隱藏,無論函數參數或傳回值是否一樣 • Ex: OverridingDemo.cpp • Overloading function乃指在同一有效範圍內,兩個函數可以有相同名稱與傳回值,只要其函數的參數相異即可.
類別中的friend沒有被繼承的性質 • 只有類別成員才能被繼承!! • Friend 並非類別成員,因此不會被衍生類別所繼承.
擴展你的程式-1 • 以上次繼承結果為這次繼承的資源 • 用繼承來撰寫物件程式的優點就是程式可依使用者須求加以擴充 • 繼承可以不斷的衍生下去 • 任何衍生類別都可成為下次繼承的基礎類別 Base class Base { }; class Derived : public Base{ }; class Third : public Derived { }; Derived Third …
擴展你的程式-2 • 設計衍生類別的constructor時,不必考慮為其祖先做設定的動作 • 衍生類別只須對上一層基礎類別負責即可 程式執行的順序 類別Base Base :: Base(…) Derived::Derived(…) Three::Three(…) 由Derived負責Based Constructor 類別 Derived 由Three負責Derived Constructor 類別 Third
我聽說C++有 多重繼承 的功能 這到底是怎麼回事???
多重繼承 • 情況: • 有兩個類別,其中一類別為 Circle用來畫各種圓形,另一類別為 Line用來畫各種線形圖形 • 現在設計者希望能提供一個圓柱類別 Cylinder,該類別應有畫圓與畫線的功能. • 我們希望能同時繼承 Circle 與 Line,這樣就可以充份利用原程式的資源
A base class . . . class Line A base class . . . class Circle 繼 承 繼 承 Cylinder 所有衍生類別同時繼承circle 與 Line
多重繼承 --程式碼範例 class A{ … }; class B{ … }; 類別 C若欲同時繼承 A 與 B 則其表示方法為: class C: public A ,public B { … }; 加入逗號即可
多重繼承 --可能的錯誤 考慮下面Code: 並不表示A與B同時為 public基礎類別,事實上,B為private基礎類別. • 若沒有明確指定,則內定值為 private class C: public A, B{ … }
多重繼承 --基礎類別的constructors設定 • 衍生類別的constructor必須考慮到其繼承類別初設的必要引數量 • class Pay : public Sal, public Pro{ … }; • Inline Pay::Pay(char* n,int Id, int per, int wr): Sal(per), Pro(n,Id) { … } 明確指定呼叫 Sal的 某個建構子 明確指定呼叫 Pro 的 某個建構子
多重繼承 --constructor呼叫的順序 • 呼叫基礎類別constructor的順序決定於當時定義衍生類別時,指明的基礎類別順序 • Class Pay: public Sal, public Pro { { … }; • Inline Pay::Pay(char* n,int Id, int per, int wr): Sal(per), Pro(n,Id) 2 1 順序對換其初設順序亦不影響
多重繼承 --模擬兩可 • 考慮下面的 Code • 基礎類別A,B同時有相同名稱的成員函數show,而類別C繼承了 A , B class A{ … public: void show(); … }; class B{ … public: void show(); … };
多重繼承 --模擬兩可 class C: public A, public B { … }; 則 void main() { C cc; cc.show(); //呼叫那一個show()? }
多重繼承 --模擬兩可 • 衍生類別 C中有兩份同名稱的show成員函數,一份來自 A, 一份來自B, Compiler 無法判斷應該呼叫那一個show function. • 解決方法: • 利用範圍運算子指明欲呼叫的成員是屬於哪一個類別 C *obj=new C(); obj-> A:: show(); obj-> B:: show(); 明確指定呼叫A的show()
End 井民全 debut.cis.nctu.edu.tw/~ching