天天看点

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

继续阅读