C 還是 C++?
C++ 中的多態是指「通過基類對象的指標或者基類對象的引用調用虛函數」,表現更多衍生類別的特性,但根據 C++ 多態的實現,我們發現這種方法存在一定的空間和效率的折損。不可否認,多態輕鬆解決了很多工程中遇到的問題,這與 pure C 的解決方案比起來,更為優雅。
在考慮移植性上,C 的光芒要蓋過 C++,但 C++ 的多態是可借鑒的,於是用 pure C 來模仿 C++ 中多態行為。
C 如何?多態
在 C 中沒有類的概念,但有 struct,而且 C 中的 struct 是不允許有函數的,只允許存在變數,那是不是函數變數就允許存在了?!所以,函數指標可以給我們一些提示。
假定有一個結構體 struct Animal 聲明如下:
struct Animal{ void (*move)();};
此時 Animal 中的 move 只是一個指標,並沒有賦值,也就是說它不能代表任何的行為,但我們可以在 main 函數中對其進行賦值,賦予相應的行為。
void Animal_move(){ printf("Animal move.\n");}
再假定一個結構體 struct Rabbit,我們可以暫且把它看作(因為 C 中沒有繼承概念)struct Animal 的衍生類別,聲明如下:
struct Rabbit{ void (*move)();};
同樣我們可以給 Rabbit 的 move 預定義一個行為:
void Rabbit_move(){ printf("Rabbit move.\n");}
struct Animal 和 struct Rabbit 在內容上完全一致,而且變數對齊上也完全一致,可以通過將 struct Rabbit 類型的指標強制轉換為 struct Animal 類型的指標,即:
Animal *panimal; Rabbit rabbit; //... panimal = (Animal*)&rabbit;
這樣,我們就可以通過 struct Animal 類型的指標或者引用來操縱 struct Rabbit 類型的對象了。
int main(void){ Animal *panimal; Rabbit rabbit; rabbit.move=Rabbit_move; panimal = (Animal*)&rabbit; panimal->move();}
Rabbit move.
請按任意鍵繼續. . .
C 多態有什麼問題
是不是有需要注意的問題?為什麼剛才特別提出「在內容上完全一致,而且變數對齊上也完全一致」?倘若把 struct Animal 的聲明作稍微的改變:
struct Animal{ int age; void (*move)();};
運行崩潰了,並不能得到上面的執行結果,原因是 move 函數指標 在struct Animal 中和 struct Rabbit 中的位移量不同,結構體是根據變數在結構體的位移量來讀取或者修改變數的。當執行 panimal->move(); 的時候,實際上引用了非法的地址:
panimal->move();
可以被形象的轉化為:
( * (panimal+sizeof(age)) ) ();
但發現 panimal 是指向 struct Rabbit 實體的,panimal+sizeof(age) 已經指向了非法地址:
| struct Rabbit 結構 |
調用 |
| void (*move )(); |
<------ rabbit.move(); |
| 非法地址 |
<------ panimal->move(); |
因此需要類比多態,必須保持函數指標變數對齊。
在一些 c 開源項目中經常用到這種設計,譬如 libevent。
搗亂 2013-05-09
http://daoluan.net