虚表指针
群里有人问。就写了
从最简单的类开始, 后面有单继承和多继承虚表指针的不同
代码在32环境下:
先写结论:
虚表指针在构造函数与析构中赋值(下面反汇编证明), 赋值: *this = 虚表指针(即首个成员)
虚表指针指向的是一个数组,存放虚函数,虚函数按一般声明先后排序.
这个数组一般在 .rdata 或 .data
每个父类都会在构造,析构中设置自己的虚表,下面继承中会写
单继承: 一个虚表 . 多继承: 有几个父类就有几个虚表
* 为什么要在构造和析构中都赋值, 在下面的继承中会写.
下面代码中顺带一些内存布局
虚表指针在构造函数中 *this = 虚表指针, 指向数组[ virtual void t ]
成员变量a 在 this + 4 的地方,跟随在虚表后, 若没有虚表的类则放在this上
#include <iostream>
#include <Windows.h>
#include <stdio.h>
using std::cout;
using std::endl;
class A
{
public:
A() {
a = 1;
}
~A() {
}
virtual void t() {
cout << "t" << endl;
}
int a;
};
int main()
{
cout <<"size:" << sizeof(A) << endl;
A a;
DWORD * ptr = (DWORD*)&a;
DWORD vptr_addr = *ptr;
//虚表在首个成员的地址上 , 即 *this
cout << "虚表:" << std::hex << vptr_addr<< endl;
// 因为vptr 占了首个成员的位置, 成员a跟在虚表后面
int member = *(ptr + 1);
cout << "member:" << member << endl;
//通过虚表找到函数地址,只有一个虚函数,因此偏移0
DWORD * arr_func = (DWORD*)vptr_addr;
void (*func)() = (void (*)())(*(arr_func + 0)); // arr_func[0]
cout << "函数地址:" << func << endl;
func();
return 0;
}
看反汇编的构造 析构函数 , 删掉了不相关的, 只看虚表指针在构造和析构中赋值:
在这2个函数中赋值都是与继承相关,防止调用错误的虚函数
A(){
mov dword ptr [this],ecx // ecx 为 this
mov eax,dword ptr [this] // eax = this
// *this = A的虚表指针 ,这里赋值
mov dword ptr [eax],offset A::`vftable' (0419B34h)
}
虚表指针: 0x00419B34
内存数据: 0x00419B34 9a 11 41 00 00 00 00 00
还原数组: [0x0041119a] -> [virtual t]
// 在析构中同样出现
~A(){
mov dword ptr [this],ecx // ecx == this
mov eax,dword ptr [this] // eax = this
// *this = A的虚表指针
mov dword ptr