事情的起頭是我某天在公司看vender的code,乍看之下發現他們一個種類的item就分成一組header file和source file,而且struct的宣告有著類似的layout,initialize的時候使用了一樣的function table structure,只是存入了各自的function。
稍微一想,欸嘿,這不是OOP嗎? 於是就有了以下的測試
#include <stdio.h> //printf
typedef struct
{
void (*f)(void);
}base;
//作為base class(?)的struct
typedef struct
{
void (*af)(void);
}A;
//當作繼承base的derived class A
typedef struct
{
void (*bf)(void);
}B;
//當作繼承base的derived class B
void printa()
{
printf("A\n");
}
//給class A的function
void printb()
{
printf("B\n");
}
//給class B的function
void print(void* ptr)
{
((base*)ptr)->f();
}
//傳入參數的type是void*是為了能夠同時接受A, B的pointer
//這裡同時也是"類似"OOP的地方
int main()
{
A a;
a.af = printa;
B b;
b.bf = printb;
print(&a); //A
print(&b); //B
return 0;
}
上面的範例是簡化過的,複雜一點的可以再自訂一個struct專門放function pointer,例如
typedef struct
{
void(*add)(void*,void*);
void(*minus)(void*,void*);
}baseMethod;
typedef struct
{
baseMethod method;
}base;
typedef struct
{
baseMethod method;
int val;
}A;
typedef struct
{
baseMethod method;
double val;
}B;
上面的範例是提供一個自訂int/double加減法的struct,接著需要initialize A和B的function table(baseMethod)。
void a_plus(void* ptr1, void* ptr2)
{
((A*)ptr1)->val = ((A*)ptr1)->val + ((A*)ptr2)->val;
}
void a_minus(void* ptr1, void* ptr2)
{
((A*)ptr1)->val = ((A*)ptr1)->val - ((A*)ptr2)->val;
}
void b_plus(voi
d* ptr1, void* ptr2)
{
((B*)ptr1)->val = ((B*)ptr1)->val + ((B*)ptr2)->val;
}
void b_minus(void* ptr1, void* ptr2)
{
((B*)ptr1)->val = ((B*)ptr1)->val - ((B*)ptr2)->val;
}
void addition(void* ptr1, void* ptr2)
{
((base*)ptr1)->method.add(ptr1,ptr2);
}
int main()
{
A a1,a2;
a1.method.add = a_plus;
a1.method.minus = a_minus;
a1.val = 1;
a2.val = 2;
//偷個懶,不init a2的method,比較好的做法應該是把宣告一個global static的method的變數
//struct裡只存放(baseMethod*)指到那個變數
B b1,b2;
b1.method.add = b_plus;
b2.method.minus = b_minus;
b1.val = 11;
b2.val = 12;
addition(&a1,&a2);
addition(&b1,&b2);
printf("%d\n",a1.val); //3
printf("%lf\n",b1.val); //23.0
return 0;
}
讓addition去call 各自的add,可以發現它會正確的call到各自的function。
這種做法最重要的是,Base的function要和其他struct對應的function有相同的memory offset,最簡單的就是都把它們宣告在struct的最一開始,所以你會看到上面的baseMethod都宣告在A跟B的最上面。
如同標題所說,這篇文章主要在講的是polymorphism(多形),inheritance只能靠programmer自己動手刻,encapsulation在C也能勉強(?)做到,就沒什麼好提的了。
說到底其實都是寫C很常玩的pointer XD
發表留言