200 likes | 302 Views
Virtual Function. Jing( 井民全). Virtual Function 基本概念. 基礎類別定義基本的功能 衍生類別 繼承了基礎類別 定義特殊的功能. class Animal. weight eat() SetWeight(). 動物的基本功能. class Cat. eat() SetWeight() Sleep(). 貓的功能. Virtual Function 基本概念. class Animal { public: int weight; void eat(); void setWeight();
E N D
Virtual Function Jing(井民全)
Virtual Function基本概念 • 基礎類別定義基本的功能 • 衍生類別 • 繼承了基礎類別 • 定義特殊的功能 class Animal weight eat() SetWeight() 動物的基本功能 class Cat eat() SetWeight() Sleep() 貓的功能
Virtual Function基本概念 class Animal { public: int weight; void eat(); void setWeight(); }; class Animal weight eat() SetWeight() 動物的基本功能 class Cat:public Animal { public: void eat(); void setWeight(); void Sleep(); }; class Cat eat() SetWeight() Sleep() 貓的功能
Cat *Mycat=new Cat(); Mycat->eat(); Virtual Function基本概念 • 貓有貓的吃飯方式! class Animal weight eat() SetWeight() 動物的基本功能 class Cat eat() SetWeight() Sleep() 貓的功能
class Animal weight eat() SetWeight() 動物的基本功能 ? class Cat eat() SetWeight() Sleep() 貓的功能 Virtual Function基本概念 • 因為貓是動物的一種, 所以我們可以用動物reference貓 Polymorphism(多形) Cat *Mycat=new Cat(); Animal *obj=Mycat; obj->eat();
class Animal Cat *Mycat=new Cat(); Animal *obj=Mycat; weight eat() virtual SetWeight() 動物的基本功能 obj->eat(); class Cat eat() SetWeight() Sleep() 貓的功能 Virtual Function基本概念 • 呼叫貓的eat(): 加入 virtual 關鍵字 virtual 範例程式: virtualConcept1.cpp
透過 Animal 只要是Animal型別就可以操作 • 可以視為 Animal 型態操作,又可呼叫自己特別function. • 泛型處理 Animal 存取未知物件 的相對應的 function
插頭必須是三孔 電壓一定要符合 220V 介面的概念 • 建立一個功能 • 只接受某種型態的物件 • 某些電器需要的是三孔插座,某些電器需要的是220V的電壓 電力公司服務 我是三孔插座 我是220V 電器1 電器2
介面的概念 • 接受電力公司提供服務的條件 • 擁有三孔插座 • 可接受 220V 電壓 若想接受服務, 則電器(物件)必須符合條件!! 3孔介面 120V電視機 220V介面
class Plug3{ virtual public void Input3() =0; } class Volate220V{ virtual public void Input220V() =0; } class TV:public Plug3,public Volate220{ public: void input3(){ } void input220V(){ } }; 把3孔轉2孔的轉換程式放在這 把220轉120V電壓轉換程式放在這 3孔介面 120V電視機 220V介面
class Sortable class Person 假設 Person 繼承 Sortable 範例程式: VirtualDestructor.dsw 虛擬解構式 Sortable *sp; Person *pp=new Person("Frank","frank@icce.rug.nl","363 3688"); // 使用基礎類別指標 reference Person sp=pp; 請問下面的程式碼,會呼叫 Sortable 還是 Person 的解構子? delete sp; <解答> 若不加入 virtual在sp的解構式中,則 delete sp 會呼叫 sp的解構子,而非 Person的解構子
虛擬解構式 • C++ 允許 解構式虛擬化, 以確定呼叫正確的解構式 所以應該在基礎類別 Sortable 的解構式中加入 virtual. class Sortable{ // < 其他程式碼 > virtual ~Sortable( ){ } } 如此 delete sp; 才會呼叫正確的 Person的解構式
class Sortable class Person 2. 執行Sortable的解構子 問題: Delete Derived 會呼叫 Derived虛擬解構式,但是程式會繼續呼叫 Base 的解構式嗎? 我們知道在 Sortable 的解構子加上 virtual後, 執行delete sp; 會呼叫目前指向物件的解構子 Sortable *sp; Person *pp=new Person("Frank","frank@icce.rug.nl","363 3688"); // 使用基礎類別指標 reference Person sp=pp; delete sp; 1. 執行Person 的解構子 解構的順序為 ~Derived() -> ~Base()
問題: 注意: 這裡重複繼承了 Base void main( ) { Derived obj; obj.getfield(); } 呼叫哪一個 Base 的 getfield( ) 多重繼承下的虛擬函式 • 一個衍生類別可能繼承多個基礎類別 考慮下面的 code class Base{ public: void setfield(int val){ field = val; } int getfield() const { return (field); } private:int field; }; class Derived: public Base, public Base{ };
重複繼承了 Vechicle 佔用了兩份空間 多重繼承下的虛擬函式 • 程式很大的時候,則會亦有類似狀況(因為基礎類別可能不是你寫的) • 多重繼承推導圖與內部結構圖
完整程式範例: VirtualDerived.dsw 解決方法: 虛擬繼承Virtual Base classes • 對於一個 AirAuto, 我們只須要一個 weight. class Land: virtual public Vehicle{ ...}; class Air: virtual public Vehicle{ ...}; 虛擬繼承與虛擬函數不同的地方在於 虛擬繼承完全可以在 compile-time 解析
執行時期的形態辨別 class 動物 • 一個基礎類別的指標,可能指的是衍生類別. • 那麼我們在執行的時候, 要如何知道這個基礎類別指標到底是指向哪一個物件? 動物 *p=new 貓; class 貓 重要觀念: 貓是動物的一種. 所以我們可以用動物存取貓. Why? 使得任何的動物種類的物件 都可以用 p 存取. 這就是抽象化,
C++ 的解決方案 • typeid指出目前的指標到底是那一種型態. (傳回字串告訴你) • dynamic_cast運算子來將一個指標轉換成 基礎類別型態 或 衍生類別型態. 在VC中,必須設定才允許使用 dynamic_cast. [project]->[setting...]->[C++]->[C++ Language]: Enable Run-Time Type Information(RTTI)
typeid 範例1 必須先 #include <typeinfo> 在class 中至少要有一個 virtual function 考慮下面的code #include <typeinfo> void main(){ cout << typeid(12).name() << endl; cout << typeid(3.14).name() << endl; } 會印出 int 會印出 double 看 typeid範例2.doc
dynamic_cast 範例 See dynamic_cast.doc