C語言實現OOP (ing)

    想用C來實現OOP,關鍵在於結構體。struct和OOP中的class最大區別爲默認的繼承訪問權限:struct是public的,大家都能看到,class是private的,只有指定的對象看得到。

碼農翻身裏面有一篇文章講到過用c語言來實現OOP,今天參照着它擼了一下oop的三大概念:封裝、繼承、多態。

1 封裝:意思就是把信息隱藏起來。

舉個例子,先創建一個shape結構體,然後實現create和init等方法,之後將源代碼封裝成庫。這樣一來,外部程序只能看見頭文件的接口原型,而看不到其內部結構。

  shape.h

//聲明結構體
struct Shape;
//api原型
struct Shape *Shape_create(int x,int y);
void Shape_init(struct Shape *self, int x,int y);
void Shape_move(struct Shape *self, int x,int y);
float Shape_area(struct Shape *self);
...

shape.c

struct Shape{
  int x;
  int y;
};

struct Shape *Shape_create(int x, int y){
  struct Shape *s = malloc(sizeof(struct Shape));
  s->x= x;
  s->y= y;
  return s;
}

void Shape_move(struct Shape *self, int x,int y)
{
  self->x= x;
  self->y= y;
}

float Shape_area(struct Shape *self){
  return (*self->vptr->area)(self);  //vptr成員後面會新增進去
}

main.c中調用api

int main(int argc, *argv[]){
  ...
  struct Shape *s = Shape_create(0,0);
  Shape_move(s,1,2);
  ...
}

雖然結構體成員和create、move等方法是分開寫的,看起來沒class那麼融合,可也算是實現了簡單的封裝。

2 繼承

先看下面代碼:

//Rectangle子類
struct Rectangle{
  struct Shape base;
  int length;
  int width;
};

struct Rectangle *Rectangle_create(int x,int y,int l, int w){

  struct Rectangle *r = malloc(sizeof(struct Rectangle)); //創建對象

  Shape_init((struct Shape *)r,x,y);  //繼承了Shape基類的成員函數:shape_init
  r->length = l;
  r->width = w;

  return r;
}
...

int main(){
  struct Rectangle *r = Rectangle_create(1,2,30,40);
  Shape_move((struct Shape*)r,10,20);
  ...
}

上述代碼創建了另一個結構體Rectangle。因其包含了Shape,所以稱之爲子類。其在內存中是這樣的:

繼承關係如下:

有兩個地方需要注意:

  • 在繼承Shape_init這個api時,傳進去的對象爲 r,此時需要類型轉換爲Shape基類。
  • 在Rectangle子類中Shape基類的位置需放在最前。下面舉個例子說明一下:
struct rectangle{
  int length;
  int width;
  struct shape base; //此處放在最後,會有問題
};
...
struct rectangle *r = malloc(sizeof(struct rectangle));
struct shape *tmp = (struct shape *)r; //強制類型轉換
tmp->x=110;

上述代碼中 將Shape類放在了最後。本來是將110賦值給tmp 的 x成員,實際卻是給了length成員,調試結果如下:

(r和tmp的地址一樣,只是裏頭的數據代表的類型不同)

3 多態

多態最難搞,關鍵在於理解函數指針的使用。先上代碼:

//新建虛函數表
struct shapeVtbl{
  float (*area)(struct shape *self);
  void (*draw)(struct shape *self);
}
//將虛函數表添加到Shape基類中
struct shape{
  struct shapevtbl *vptr;
  int x;
  int y;
};

新結構體shapeVtbl 包含兩個函數指針,目的是爲了獲取對象的面積及畫圖,我們稱其爲虛函數表。之後不管創建的子類是Rectangle 還是其它的,只要是繼承自Shape基類,都會有個虛函數表。

下面是Rectangle 和Square對象。前者的函數指針area指向的是自家的 Rectangle_area();而Square對象的函數指針area指向的是Square_area()。

開頭部分的shape.h中有個api爲Shape_area(...),在子類繼承之後,調用它就可以直接指向自家的api地址。如下:

float Area;
...
struct Rectangle *r = Rectangle_create(1,2,30,40);
Area = Shape_area((struct Shape*)r);

注意:上圖中的虛函數表需要你手動將自家的函數入口地址填進去,比如 &Rectangle_area 和 &Rectangle_draw,而C++則可以自動完成。

參考 https://mp.weixin.qq.com/s/2ivQ9hcRvZnhk89jzAppSg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章