2024年1月30日 星期二

C語言使用物件導向(面向對象) - 下 多態(型)

理解了指標以及函式指標,我們再來談談多態,就能更容易理解了,多態的直白意思即,一樣的事不同做法;相同名稱的方法,或是說介面,藉由給他不同的參數,就會做不一樣的事。比如人的跑是用兩條腿跑,狗的跑是用四條腿跑,同樣的"跑",執行的過程結果是不一樣的。

「多型是指向函式的指標」的一種應用,函式指標只是一個軀殼,放入不同的靈魂,則執行不同的結果,軀殼可以抽換不同的靈魂。

多態範例先拿Linux的FILE結構為例,Linux對於裝置都是看作是檔案,為了統一介面,Linux制定了以下操作檔案的介面。5個function pointer,本質上是5個inteface,需要實體function來填充它;本質上是5個軀殼,需要靈魂來填入。

struct FILE {
  void (*open)(char* name, int mode);
  void (*close)();
  int (*read)();
  void (*write)(char);
  void (*seek)(long index, int mode);
};

前面幾行是實體function(靈魂),接下來指標(軀殼)放入function的位址,在最後一行程式碼。

#include "file.h"

void open(char* name, int mode) {/*...*/}
void close() {/*...*/};
int read() {int c;/*...*/ return c;}
void write(char c) {/*...*/}
void seek(long index, int mode) {/*...*/}

struct FILE console = {open, close, read, write, seek};

仿效上述提到的Linux FILE struct, 建立struct PointOps,裡頭放函式指標void (*display)(struct Point*);再把此結構指標放進struct Point裡,這兩層可以都算是介面,一個是行為的群組,一個是display的軀殼。

point.c

#include "point.h"
#include <stdlib.h>
#include <math.h>

struct PointOps {
  void (*display)(struct Point*);
};

struct Point {
  double x,y;
  PointOps* ops;
};

struct NamedPoint {
  struct Point point;
  void* priv;
};

下面程式碼先看到最後6行,先建立了兩個display的實現function,function的位址再放進struct PointOps的結構中,function位址就是靈魂,這樣會在記憶體中劃開兩個function的空間,靈魂放到建立的結構實體中。再將這實體結構的位址,賦值給malloc出來的point實體物件中的ops,其就是用來存指標的指標變數。

struct Point* makepoint(double x, double y) {
  struct Point* p = malloc(sizeof(struct Point));
  p->x = x;
  p->y = y;
  p->ops = &ops_point;
  return p;
}

struct NamedPoint* makeNamedPoint(double x, double y, char* name) {
  struct NamedPoint* p = malloc(sizeof(struct NamedPoint));
  p->point.x = x;
  p->point.y = y;
  p->priv = malloc(sizeof(struct NamedPointPriv));
  strcpy((p->priv)->name, name);
  p->point.ops = &ops_NamedPoint;
  return p;
}

static void do_displayPoint(struct Point* point)
{ ... }
static void do_displayNamedPoint(struct Point* point)
{ ... }
static PointOps ops_point = {do_displayPoint};
static PointOps ops_NamedPoint = {do_displayNamedPoint};

在調用point物件的時候,因為介面已經統一,只要調用一樣的介面,不同的物件,就會調用在創建物件時,所指定的最後實作函式(靈魂)

void displayPoint(struct Point* point)
{
    point->ops->display(point);
}

0 意見:

張貼留言