170 likes | 251 Views
C 語言實做 C++ 物件導向特性. 封裝繼承與多型. C 也可以很 OO(Object Oriented). 善用 struct 語法對應與命 名規則 瞭解 C++ 在背後幫我們做了哪些事. 善用 Struct. 一個資料結構必定有多個操作的演算法 ( 反之不一定 ) 這個演算法正是對於資料結構的操作方法 (Method/Manipulation) 在一個二元搜尋樹尋找一個 key ,尋找的演算法就是二元搜尋樹的一種操作。 我們把水放入杯子,杯子是一個資料結構,具有儲存水的演算法。. 語法對應與命名規則.
E N D
C語言實做C++物件導向特性 封裝繼承與多型
C也可以很OO(Object Oriented) • 善用struct • 語法對應與命名規則 • 瞭解C++在背後幫我們做了哪些事
善用Struct • 一個資料結構必定有多個操作的演算法(反之不一定) • 這個演算法正是對於資料結構的操作方法(Method/Manipulation) • 在一個二元搜尋樹尋找一個key,尋找的演算法就是二元搜尋樹的一種操作。 • 我們把水放入杯子,杯子是一個資料結構,具有儲存水的演算法。
語法對應與命名規則 • C沒有Reference,所有的Reference都要成為指標,指標仍然是指標。 • 沒有運算子多載,必須以全名表達。 • 比如索引運算子以indexOf或者index_of • C++ Method是一種針對物件的操作,Compiler偷偷塞了this指標。 • 我們要讓function第一個參數為this • int& Class::getData(int value); • int* class_get_data(Class* pthis,int value); • int& Class::getData(int value) const; • int* class_get_data(const Class* pthis,int value);
語法對應與命名規則 ( cont. ) • 所有的命名規則來自於對於資料結構操作方法的描述: • Class的GetData • C++: Class::getData • C++以小寫開頭,之後每個單字為大寫開頭 • C: class_get_data() • C以小寫描述資料結構名稱,再以底線分隔單字
語法對應與命名規則 ( cont. ) • 從範例看到 • C++保證不會更改到this的函式 • int& Class::getData(int idx) const; • 轉為C之後即是將this指標設為const • int* class_get_data(const Class* pthis,int idx);
以C實做繼承以及多型 • C++具有物件導向程式語言的威力,封裝繼承與多型 • C語言實做封裝容易,只要將method傳this以及更改命名方式即可。
以C實做繼承以及多型 (cont.) • 進行繼承,我們要瞭解到: • 每一次的繼承都會保留父代的資料,因此子代會越來越大。 • 父代函式可以叫用再子代的物件。 • 父代的可複寫函式是透過Virtual Table管理的。 • 建構子與解構子順序。 • 解構子是複寫的。
以C實做繼承以及多型 (cont.) • 首先來看前兩點 • 每一次的繼承都會保留父代的資料,因此子代會越來越大。 • 父代函式可以叫用再子代的物件。 • 我們可以想像第二點會是第一點的延伸。 • 父代函式作用再子代,是來自於子代有一份父代Class的資料。
以C實做繼承以及多型 (cont.) 再C語言可以藉由簡單的data link(data layout)方式,讓父代盡可能擺在第一個位置。 typedef struct SuperClass{ int data; int someArray[ 16 ]; }SuperClass; typedef struct ChildClass{ SuperClass super; int dataOfChild; }ChildClass;
以C實做繼承以及多型 (cont.) • 接著看以下的敘述 • 父代的可複寫函式是透過Virtual Table管理的。 • 建構子與解構子順序。 • 解構子是複寫的。 • 關於Virtual Table,我們知道他被稱為Dynamic Binding,執行期決定function為何。 • 恰好function pointer為一種可以被當作函式叫用,且可以執行期間改變的pointer。
以C實做繼承以及多型 (cont.) • Function pointer群的管理,我們可以將所有的function pointer放一份到Super Class • 缺點: • 每次複寫需要改變很多指標 • 每個子代物件都有同樣的一大堆指標,且內容重複。
以C實做繼承以及多型 (cont.) • 經由所有相同Class的物件的Function Pointer都是重複這點看來 • 再該代版本只有一個。 • 他被稱為virtual table • 因此可以想見那確實是一個表格,一個靜態存在的表格。
以C實做繼承以及多型 (cont.) • 我們可以想像他大概長這樣 typedef struct SuperClassVirtualTable { void (*superMethod)(Super*); void (*superFinal)(Super*) } SuperClassVirtualTable; • 在程式定義區域應該會有一些實做的Function,實做過後會有一個靜態實體指向這些Function,也就是: static void super_method_virtual(Super* s){} static void super_final_virtual(Super* s) {} SuperClassVirtualTable superVirtualTable = { super_method_virtual, super_final_virtual };
以C實做繼承以及多型 (cont.) • 既然一個Class只有一份,那麼Class中就不需要存有多個pointer • 只需要一個指向Class的函式表指標即可。 typedef struct SuperClass{ int data; int someArray[ 16 ]; void* virtualTable; }SuperClass;
以C實做繼承以及多型 (cont.) • 為了達成父代函式未被複寫的版本仍為原先的版本,設定virtual table的工作應該包含在init函式。 • 如何叫用複寫函式? • 透過virtualTable • ((SuperClassVirtualTable*)this->virtualTable)->superMethod(this); • 由於函式表將來會被覆寫,因此父代的函式可以透過函式表找到子代覆寫的版本。 • 達成多型效果。
以C實做繼承以及多型 (cont.) • 建構子順序 • 父代建構後才建構子代 • 在子代的init必須先叫用父代的init • 解構子順序 • 子代解構後才解構父代 • 在子代的final先處理自己的資料 • 之後再執行父代final