天天看點

用C來模拟實作多态?

如何用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;
}      

繼續閱讀