Java 中有 interface 關鍵字,C++ 中有抽象類或純虛類能夠與 interface 比擬,C 語言中也能夠實現相似的特性。編程
接口和抽象類有什麼區別?數據結構
不少編程書籍也常常說要面向接口編程,個人理解是,接口強制派生類必須實現基類(接口)定義的契約,而抽象類則容許實現繼承從而致使派生類能夠不實現(重寫)基類(接口)定義的契約。一般這不是問題,但在有一些特定的狀況,看起來不那麼合適。函數
好比定義一個 Shape 基類,其中定義一個 draw() 方法,給一個什麼都不作的默認實現(一般是空函數體),這實際沒有任何意義。spa
再好比基類改變某個方法的實現,而派生類採用實現繼承並無重寫這個方法,此時可能會致使一些奇怪的問題。以鳥爲例,基類爲 Bird ,咱們可能會定義一個 fly() 方法,一個 walk() 方法,由於有的人認爲鳥既能夠走又能夠飛。開始時咱們在 walk() 的實現裏做了假定,認爲雙腳交叉前進纔是 walk ,但是後來發現有些鳥是雙腳一齊蹦的,不會交叉前進。這個時候怎麼辦?基類 Bird 的 walk() 方法是否要修改、如何修改?.net
在 C++ 中,沒有接口關鍵字 interface ,同時爲了代碼複用,常常採用實現繼承。在 C 語言中,咱們前面幾篇文章討論了封裝、隱藏、繼承、虛函數、多態等概念,雖然均可以實現,但使用起來總不如自帶這些特性的語言(如 C++ 、Java )等駕輕就熟。一旦你採用咱們前面描述的方法來進行面向對象編程,就會發現,在 C 語言中正確的維護類層次是一件很是繁瑣、容易出錯的事情,並且要比面向對象的語言多寫不少代碼(這很容易理解,面嚮對象語言自帶輪子,而 C 要本身造輪子,每實現一個類都要造一遍)。但有一點,當咱們使用 C 語言做面向對象編程時,比 C++ 有明顯的優點,那就是接口。指針
接口強制派生類實現,這點在 C 中很容易作到。並且咱們在編程中,實際上多數時候也不須要那麼多的繼承層次,一個接口類做爲基類,一個實現類繼承接口類,這基本就夠了。在 C 語言中採用這種方式,能夠不考慮析構函數、超過 3 層繼承的上下類型轉換、虛函數調用回溯、虛函數表裝配等等問題,咱們所要作的,就是實現基類接口,經過基類指針,就只能操做繼承層次中最底層的那個類的對象;而基類接口,天生就是不能實例化的(實際上是實例化了沒辦法使用,由於結構體的函數指針沒人給它賦值)。orm
一個示例以下:server
[cpp] view plain copy對象
struct base_interface { blog
void (*func1)(struct base_interface* b);
void (*func2)(struct base_interface* b);
int (*func_3)(struct base_interface* b, char * arg);
};
struct derived {
struct base_interface bi;
int x;
char ch;
char *name;
};
上面是頭文件,derived 結構體經過包含 base_interface 類型的成員 bi 來達到繼承的效果;而 base_interface 沒法實例化,咱們沒有提供相應的構造函數,也沒有提供與 func_1 , func_2 等函數指針對應的實現,即使有人 malloc 了一個 base_interface ,也沒法使用。
derived 類能夠提供一個構造函數 new_derived ,同時在實現文件中提供 func_1 , func_2 ,func_3 的實現並將函數地址賦值給 bi 的成員,從而完成 derived 類的裝配,實現 base_interface 定義的契約。
示例實現以下:
[cpp] view plain copy
static void _derived_func_1(struct base_interface *bi)
{
struct derived * d = (struct derived*)bi;
d->x *= 2;
printf("d->name = %s\n", d->name);
}
/* _derived_func_2 impl */
/* _derived_func_3 impl */
struct derived *new_derived()
{
struct derived *d = malloc(sizeof(struct derived));
d->bi.func_1 = _derived_func_1;
d->bi.func_2 = _derived_func_2;
d->bi.func_3 = _derived_func_3;
d->x = 0;
d->ch = 'a';
d->name = NULL;
return d;
}
咱們能夠這麼使用 base_interface 接口:
[cpp] view plain copy
void do_something(struct base_interface *bi)
{
bi->func_1(bi);
}
int main(int argc, char **argv)
{
struct derived * d = new_derived();
do_something((struct base_interface*)d);
return 0;
}
上面的代碼中 do_something 函數徹底按照接口編程,而 bi 能夠實際指向任意一個實現了 base_interface 接口的類的實例,在必定程序上達到多態的效果,花費的代價至關小,卻可讓咱們的程序提升可擴展性,下降耦合。
這種簡單的方法也是我在本身的項目中使用的方法,效果不錯。
好啦,C 語言面向對象編程系列的基礎性介紹就告一段落,下面是前幾篇的連接,有興趣的能夠回頭看看:
接下來我會提供幾個實做的例子,包括基本的數據結構,如單鏈表、樹,還有一個 http server 的例子。
網友評論:
既然用到了malloc那應該也寫個free吧?
謝謝提醒,實際使用時,free在別處。