#include <cstdio>
class Base {
public:
virtual void f() { cout << "Base::f" << endl; }
virtual void g() { cout << "Base::g" << endl; }
void h() { cout << "Base::h" << endl; }
};
typedef void(*Fun)(void); //函数指针
int main()
{
Base b;
// 这里指针操作比较混乱,在此稍微解析下:
// *****printf("虚表地址:%p\n", *(int *)&b); 解析*****:
// 1.&b代表对象b的起始地址
// 2.(int *)&b 强转成int *类型,为了后面取b对象的前四个字节,前四个字节是虚表指针
// 3.*(int *)&b 取前四个字节,即vptr虚表地址
//
// *****printf("第一个虚函数地址:%p\n", *(int *)*(int *)&b);*****:
// 根据上面的解析我们知道*(int *)&b是vptr,即虚表指针.并且虚表是存放虚函数指针的
// 所以虚表中每个元素(虚函数指针)在32位编译器下是4个字节,因此(int *)*(int *)&b
// 这样强转后为了后面的取四个字节.所以*(int *)*(int *)&b就是虚表的第一个元素.
// 即f()的地址.
// 那么接下来的取第二个虚函数地址也就依次类推. 始终记着vptr指向的是一块内存,
// 这块内存存放着虚函数地址,这块内存就是我们所说的虚表.
//
printf("虚表地址:%p\n", *(int *)&b);
printf("第一个虚函数地址:%p\n", *(int *)*(int *)&b);
printf("第二个虚函数地址:%p\n", *((int *)*(int *)(&b) + 1));
Fun pfun = (Fun)*((int *)*(int *)(&b)); //vitural f();
printf("f():%p\n", pfun);
pfun();
pfun = (Fun)(*((int *)*(int *)(&b) + 1)); //vitural g();
printf("g():%p\n", pfun);
pfun();
}
虚函数是怎么实现多态的过程的
//https://www.cnblogs.com/yc_sunniwell/archive/2010/07/12/1775637.html
如上资料所述, 虚函数的多态过程实现是通过虚函数表来实现的, 虚函数表就是存放类中虚函数地址,而子类覆盖了父类的虚函数以后,覆盖的结果就是在父类原本的地址上把这个函数换成子类覆盖后的虚函数地址, 这样, 父类指针在调用虚函数的时候就是调用的字了覆盖的虚函数, 从而实现了运行时的多态.
虚函数表的存储顺序为先到先进, 也就是先父类后子类, 如果子类覆盖了父类的那么子类的方法会替换原来虚表中父类的方法
当存在多重继承的时候, 一个子类继承了多个父类, 那么每个父类都有自己的虚表, 子类成员函数被放到第一个父类表中,如果有覆盖父类的虚函数那么产生如下结果
1. 如果这个方法在每个父类中都有, 那么每个父类中都会被覆盖
2. 如果只有单个父类那么只会覆盖单个父类
基于上述问题我们提出了安全性问题
多态性可以通过父类的指针调用子类的方法, 那么如果子类的方法不是父类中的虚函数, 是自己本身产生的那么直接父类方法调用的时候会报错, 所以我们可以通过虚函数表的存储来调用子类的虚函数,尽管这个虚函数是父类不拥有的, 照样还是可以调用, 这就跟安全性相悖.