多型 Polymorphism 講師 : 洪安 1
多型 編譯時期多型 ( 靜態多型 ) function overloading 如何正確呼叫同名的函數? 利用參數個數與型態 operator overloading 其實同 function overloading 執行時期多型 ( 或動態多型 ) 如何正確呼叫不同物件的相同名稱的成員函數 利用繼承與多型 2
子類別與父類別物件間的指定 (assignment) class base { int x ; setx(int n) { x=n;} class derived: public base { int y ; setx(int n) { base::setx(3*n);} sety(int n) { y = n;} 3 int main() { base b ; derived d ; b = d ; // 可以嗎? Y b.setx(5) ; // 哪個 setx() B b.sety(10); //? X d = b ; //? X d.setx(5) ; //D d.sety(8); // D return 0; }
課堂練習 :4_assignment.cpp class base { int x ; void setx(int n) { x=n;} }; class derived: public base { int y ; void setx(int n) { base::setx(3*n);} void sety(int n) { y = n;} 結論 base Obj = derived Obj derived Obj = base Obj 4
衍生類別的指標 Case 1 int main() { base *pb ; base b; derived d ; pb = &b ; // Sure! pb->setx(5) ; } pb = &d ; // 可以嗎? Y pb->setx(5) ; // 指到哪個? B pb->sety(10); // X return 0; Case 2 int main() { derived *pd ; base b; derived d ; pd = &b ; //??? X pd->setx(5) ; //? D return 0; } pd = (derived *) &b 5
衍生類別的參考 (reference) Case 1 int main() { base b; derived d ; base &refb1 = b ; refb1.setx(5) ; // 哪個? B } base &refb2 = d ; // 可以嗎? Y refb2.setx(5) ; // 指到哪個? B refb2.sety(10); //X return 0; Case 2 int main() { base b; derived d ; derived &refd1 = b ; // 可以嗎? X refd1.setx(5) ; // 指到哪個? D return 0; } derived &refd1 = (derived &) b ; 6
課堂練習 4_PtrRef.cpp class base { int x ; void setx(int n) { x=n;} class derived: public base { int y ; void setx(int n) { base::setx(3*n);} void sety(int n) { y = n;} base *pb ; base b; derived d ; 7
多型與虛擬函數 (virtual function) 是否能呼叫到正確的 move() 與 stop(); class car { move() ; stop(); class Benz: public car { move() ; stop(); 甚麼是執行時期的多型? class Volvo: public car { move() ; stop(); class Civic: public car { move() ; stop(); int main() { Benz b; Volvo v; Civic c ; demo(b); demo(v); demo(c) ; return 0; } void demo(car& c) { c.move() ; c.stop() ;} 8
不使用虛擬函數 class car { void move() { cout << car move ;} class Benz: public car { void move() { cout << Benz move ;} class Volvo: public car { void move() { cout << Volvo move ;} void demo(car& c) { c.move() ; } int main() { Benz b ;Volvo v; demo(b); demo(v) ; return 0;} 實際 try! 輸出結果為何? 9
課堂練習 : 4_NoVirtual.cpp 實作類別 car, volvo, benz 實作成員函數 move 實作函數 demo 10
什麼是虛擬函數 (virtual function)? 是一種宣告在基底類別中的成員函數 提供執行時期多型的機制 使用 virtual 關鍵字 通常衍生類別會 override 它 11
使用虛擬函數 ( 配合 reference) class car { virtual void move() { cout << car move ;} class Benz: public car { void move() { cout << Benz move ;} class Volvo: public car { void move() { cout << Volvo move ;} void demo(car& c) { c.move() ; } void main() { Benz b ;Volvo v; demo(b); demo(v) ; } 實際 try! 輸出結果為何? 12
使用虛擬函數 ( 配合 pointer) class car { virtual void move() { cout << car move ;} class Benz: public car { void move() { cout << Benz move ;} class Volvo: public car { void move() { cout << Volvo move ;} void demo(car *pc) { pc->move() ; } void main() { Benz b ;Volvo v; demo(&b); demo(&v) ; } 實際 try! 輸出結果為何? 13
使用虛擬函數 ( 配合物件傳遞 ) class car { virtual void move() { cout << car move ;} class Benz: public car { void move() { cout << Benz move ;} class Volvo: public car { void move() { cout << Volvo move ;} void demo(car c) { c.move() ; } int main() { Benz b ;Volvo v; demo(b); demo(v) ; return 0; } 實際 try! 輸出結果為何? 14
不使用多型可以嗎? 多型 : 一個介面多種用法 void move(car& c) { c.move() ;.} 不多型 : void move(void *p, int type) { } 優點? 缺點? switch(type){ } case 1: ((Benz *)p)->move(); break ; case 2: ((Volvo *)p)->move(); break ; 15
範例討論 Stack 先進後出 Queue 先進先出 list head store(x); retrieve(); #1 #2 #n 如何利用 list 來模擬 ( 實作 ) stack 與 queue stack queue retrieve store #1 retrieve #1 #2 #n store 16 #n-1 #n
繼承示意圖 class list { virtual void store(int i) ; virtual int retrieve() ; class stack: public list { void store(int i) ; int retrieve() ; class queue: public list { void store(int i) ; int retrieve() ; 17
更多虛擬函數的細節 純粹虛擬函數 (pure virtual function) class printer { string filename ; void reset() { } virtual void print(int m)=0 ; } 抽象類別 (abstract class) 當類別至少含有一個純粹虛擬函數時 不能用來產生物件 e.g. printer p ; //Error 18
純粹虛擬函數的內容 純粹虛擬函數 (pure virtual function) class printer { string filename ; virtual void reset()=0 { filename= test.txt ; } virtual void print(int mode)=0 ; } 19
純粹虛擬函數的特性 衍生類別一定要 override 基底類別中所有的純粹虛擬函數 class printer {...}; class HPLaserJet6L: public printer { void reset() { 自己的版本 } void print(int mode) { 自己的版本 } }; 20
抽象類別的用途 ( 一 ) 設計共同的使用介面的類別 ( 衍生類別負責實作 ) class shape { string name ; virtual void draw(char b[][80])=0 ; // 不必有實作 virtual void clear()=0 ; // 不必有實作 }; class triangle: public shape { class circle: public shape { }; 21
抽象類別的用途 ( 二 ) 防止使用者產生不允許存在的物件 class shape { string name ; virtual void draw()=0 ; }; class triangle: public shape { class circle: public shape { }; int main() { shape s ; /* 這是啥米碗糕 */.. } 22