关于虚函数表指针的个人理解
[以下均在VS2022 x64平台处实现]
son内的内存分布:
son的虚函数表是对继承的类的虚函数表进行拷贝, 此后的修改在这份拷贝的表上进行;
排序方式以继承的顺序有关, 新添加的虚函数默认在第一个虚函数表里添加
#include <iostream>
using namespace std;
class Father {
public:
//Father(int x = 100, int y = 200) {};
virtual void play1() { cout << "调用Father::play1()" << endl; };
virtual void play2() { cout << "调用Father::play2()" << endl; };
void play3() { cout << "调用play3()" << endl; };
int x = 100;
int y = 200;
static int z;
};
class Monther {
public:
virtual void cooking1() { cout << "调用Monther::cooking1()" << endl; }
virtual void cooking2() { cout << "调用Monther::cooking2()" << endl; }
virtual void cooking3() { cout << "调用Monther::cooking3()" << endl; }
int m = 600;
int n = 700;
};
class Son : public Father , public Monther{
virtual void play1() { cout << "调用Son::play1()" << endl; }
virtual void play3() { cout << "调用Son::play3()" << endl; }
virtual void cooking1() { cout << "调用Son::cooking1()" << endl; }
};
typedef void(*func_t)(void);
typedef long long LL;
int main() {
Son son;
//由于虚函数指针有8字节, 所以当我们使用(int*)类型读取时会丢失高位4字节的信息
//即使你只使用了低4字节的存储,计算机仍旧判断为指针异常
//所以"int* vfptr_father = (int*)*(int*)&son;"会抛出
//"0x00007FF76111290E 处(位于 Project12.exe 中)引发的异常: 0xC0000005: 读取位置 0x000000006111BD88 时发生访问冲突"
LL* vfptr_father = (LL*)*(LL*)&son;
for (int i = 0; i < 3; i++) {
cout << "正在调用第一个虚函数表的第" << i + 1 << "个函数:\t";
((func_t) * (vfptr_father + i))();
}
for (int i = 0; i < 2; i++) {
//在执行"*(int*)((LL)&son + 8 + 4 * i);"时,电脑先以8字节的内存来进行指针的算术运算
//但我们知道所读取的数据是int类型4字节的数据,所以将其LL类型的地址(不是LL*)转化为(int*),
//限定计算机读取内存的范围, 这样便能读取到我们所需的数据而不会多读取或少读取
cout << "正在输出第" << i + 1 << "个成员变量的值:\t" << *(int*)((LL)&son + 8 + 4 * i) << endl;
}
LL* vfptr_monther = (LL*)*(LL*)((LL)&son + 16);
for (int i = 0; i < 3; i++) {
cout << "正在调用第二个虚函数表的第" << i + 1 << "个函数:\t";
((func_t) * (vfptr_monther + i))();
}
for (int i = 0; i < 2; i++) {
//因为64位平台的虚函数指针是8字节, 而(int*)类型的指针只读取4字节,
//所以虚函数指针要读取两次才能到第一个变量处
cout << "正在输出第" << i + 3 << "个成员变量的值:\t" << *((int*) & son + 6 + i) << endl;
}
return 0;
}