C++ 虚函数表、函数地址、内存布局解析
先看一下虚函数:
class Base { public: virtual void Funtest1(int i) { cout << "Base::Funtest1()" << endl; } virtual void Funtest2(int i) { cout << "Base::Funtest2()" << endl; }
void common(int i)
{
cout << "Base::common()" << endl;
}
int _data; }; int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; return 0; }
8?不知道大家有没有问题,反正我是有疑惑了。以前在对象模型(https://blog.csdn.net/qq_39412582/article/details/80808754)时我提到过怎么来求一个类的大小。按照那个方法,这里应该是4才对啊,为什么会是8呢?
通过观察。我们发现这个例子里面和以前不一样,类成员函数变成了虚函数,这是不是引起类大小变化的原因呢?
我们假设就是这样,然后看看内存里是怎么存储的呢?
可以看到它在内存里多了四个字节,它是一个指针,存储的是虚拟数列表的指针;
我们来验证一下:
typedef void(__stdcall *PVFT)(int); //函数指针 int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; PVFT* pVFT = (PVFT*)(*((int*)&b)); while (*pVFT) { (*pVFT)(1); pVFT += 1; } // 成员变量 _data int * pt =(int*) &b; pt += 1; cout << (*pt) << endl; return 0; }
可以看到 C++ 虚函数表,以及成员在内存中的布局;
派生类虚表:
1.先将基类的虚表中的内容拷贝一份
2.如果派生类对基类中的虚函数进行重写,使用派生类的虚函数替换相同偏移量位置的基类虚函数
3.如果派生类中新增加自己的虚函数,按照其在派生类中的声明次序,放在上述虚函数之后
https://coolshell.cn/articles/12176.html
多态缺陷
●降低了程序运行效率(多态需要去找虚表的地址)
●空间浪费
成员函数地址:
typedef void (Base::*fun)(int); typedef void(__stdcall *PVFT)(int); //函数指针 int main() { cout << sizeof(Base) << endl; Base b; b._data = 10; PVFT* pVFT = (PVFT*)(*((int*)&b)); while (*pVFT) { (*pVFT)(1); pVFT += 1; } // 成员变量 _data int * pt =(int*) &b; pt += 1; cout << (*pt) << endl; // 赋值 fun f = &Base::common; //使用 (b.*f)(1); return 0; }
fun f = &Base::common; (b.*f)(1);
当需要根据函数名来响应处理时
结果:
如果我们想取函数地址怎么办,直接强转会报错
unsigned int a = reinterpret_cast<unsigned int>(f);//编译错误
其实我们知道类成员函数只是函数第一个参数是this指针,但是this指针不是通过普通函数参数压栈方式,而是放进ecx中。