C++ 虚函数表与多态 —— 继承的虚函数表 & 内存布局
1. 使用继承的虚函数表:
如果不涉及多重继承,每个类只有1个虚函数表,当子类继承父类后,子类可以自己改写和新增虚函数,如下图所示:
子类重写 func_1 后,子函数的 func_1 将会有新的逻辑,不会干扰到父类;
子类新增行的 func_4 方法后,父类无法访问到该方法。
如下代码:
1 #include <iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 virtual void func_1() { cout << "Father::func_1"; } 8 virtual void func_2() { cout << "Father::func_2"; } 9 virtual void func_3() { cout << "Father::func_3"; } 10 public: 11 int x = 666; 12 int y = 999; 13 }; 14 15 class Son : public Father 16 { 17 public: 18 void func_1() { cout << "Son::func_1 "; } //重写了虚函数 19 virtual void func_4() { cout << "Son::func_4 "; } //子类对象新写的虚函数 func_4 20 }; 21 22 typedef void(*func_t)(void); 23 24 int main(void) 25 { 26 Father father; 27 cout << "father 对象地址:" << (int*)&father << endl; 28 int* vptr_f = (int*)*(int*)&father; //虚表指针 29 30 for (int i = 0; i < 3; i++) //这里如果想循环4次往下访问,不会访问到子类的 func_4 ,会访问到非法内存 31 { 32 cout << "调用第" << i + 1 << "个虚函数:"; 33 ((func_t) * (vptr_f + i))(); 34 cout << "地址为:" << *(int*)(vptr_f + i) << endl; 35 } 36 37 cout << "==============================================" << endl; 38 39 Son son; 40 cout << "son 对象地址:" << (int*)&son << endl; 41 int* vptr_s = (int*)*(int*)&son; //虚表指针 42 43 for (int i = 0; i < 4; i++) 44 { 45 cout << "调用第" << i + 1 << "个虚函数:"; 46 ((func_t) * (vptr_s + i))(); 47 cout << "地址为:" << *(int*)(vptr_s + i) << endl; //打印每个虚函数的地址 48 } 49 50 for (int i = 0; i < 2; i++) 51 { 52 cout << *(int*)((int)&son + 4 + i * 4) << endl; 53 } 54 cout << "sizof(son)==" << sizeof(son) << endl; 55 }
运行结果:
子类的虚函数表生成:
看上图可知,子类对象的 func_1() 地址与父类的 func_1() 地址便可知,重写的虚函数并不是把父类的同名函数覆盖改写,而是在新内存新写。
子类对象是先将父类虚函数表的成员地址全部复制下来,如果子类重写了父类的某个虚函数,便将被重写虚函数的指针,改为新虚函数的指针(指针替换)。
如果子类增加了新的虚函数,那么就会把这个虚函数的地址添加到虚函数表的表尾部。
注意:如果仔细观察我们可以发现,上图中按顺序走的虚函数所存放的地址不同,Father的3个func函数地址不是线性排序。
===========================================================================================================