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

结果:

用C来模拟实现多态?_虚表