如何用C來模拟實作多态?
要實作多态,首先我們應充分了解多态的原理。
C++是如何實作多态的?
在基類的函數前加上virtual關鍵字,在派生類中重寫該函數,運作時将會根據對象的實際類型來調用相應的函數。如果對象類型是派生類,就調用派生類的函數;如果對象類型是基類,就調用基類的函數。
也就是說:
如果類中包含了虛函數–>類對象大小+4個位元組–>位址(虛表)–>虛函數集合(虛函數表)
基類虛表:按照基類虛函數的聲明次序将其添加到基類的虛表中
派生類:
1、将基類虛表中記憶體拷貝一份
2、如果派生類重寫基類中某個虛函數,用派生類自己的虛函數替換虛表中相同偏移量位置的基類虛函數
3、将派生類自己的虛函數添加在虛表的最後
多态調用原理(條件滿足):
1、确認是否為虛函數–>否(call)
2、從對象的前四個位元組取虛表位址–>虛函數位址
3、傳參
4、調用
我們可以用結構體和函數指針實作,如下:
// 幾個基本的函數,對應抽象類中的虛函數
void foo1()
{
printf("base foo1 \r\n");
}
void foo2(int i)
{
}
void foo3(int i, int j)
{
}
// 虛表,包含了虛函數的指針
typedef struct Vtbl
{
void(*pf1)();
void(*pf2)(int);
void(*pf3)(int, int);
}Vtbl;
// 類的虛表
Vtbl g_vtbl = { &foo1, &foo2, &foo3, };
// 基類,開始處是虛表指針,後面是結構成員。
typedef struct Base
{
//Vtbl *pvtbl;
void *pvtbl;
int field1;
int field2;
}Base;
// 構造函數
// 也是一個普通的成員函數,需要一個this指針
void InitBase(Base *p)
{
p->pvtbl = &g_vtbl;
p->field1 = 0x1234;
p->field2 = 0x5678;
}
// 子類中的虛函數,重載了父類中的同類型函數
void Sfoo1()
{
// 可以考慮調用父類中的函數
// foo1();
printf("derive foo1 \r\n");
}
void Sfoo4(char *s)
{
printf("hello %s\r\n", s);
}
// 子類中的虛表,因為記憶體布局是一樣的
// 直接使用父類的
typedef struct SVtbl
{
Vtbl vtbl;
void(*pf4)(char *);
}SVtbl;
// 子類的虛表
SVtbl g_svtbl = { { &Sfoo1, &foo2, &foo3, }, &Sfoo4, };
// 某個子類,包含父類的内容
// 還有自己的成員
typedef struct Derive
{
Base a;
int field3;
}Derive;
// 構造函數
// 也是一個普通的成員函數,需要一個this指針
void InitDerive(Derive *p)
{
InitBase((Base*)p);
p->a.pvtbl = &g_svtbl;
p->field3 = 0xabcd;
}
void TestPolymorphism(Base *p)
{
// 雖然使用的是父類型的指針
// 但虛函數最終會根據對象的真實類型調用。
(*((Vtbl*)p->pvtbl)->pf1)();
}
void TestVtbl()
{
// 子類的一個對象
Derive s;
Base *p = 0;
// 初始化
InitDerive(&s);
p = (Base*)&s;
// 調用Sfoo4
((SVtbl*)p->pvtbl)->pf4("Sfoo4");
// 測試多态
TestPolymorphism((Base*)&s);
}
int main()
{
TestVtbl();
system("pause");
return 0;
}