虚函数表分析
结合上一次探究的代码,我们可以得到父类对象和子类对象内存布局示意图
对于上图的总结
-
包含虚函数的类才会有虚函数表,同属于一个类的对象共享这个虚函数表,但是各个对象都有各自的
vptr(虚函数表指针)
,但是该指针所指向的地址相同(虚函数表首地址) -
父类中有虚函数子类中也会有虚函数,即父类中有虚函数表子类中也会有虚函数表,即便子类并没有覆盖父类的任何虚函数
class Derive : public Base { public: // 空 };
sizeof(Derive) = 4 // 说明子类中有虚函数表指针
但是,对于此类情况,无论父类还是子类,都只会各自拥有一个虚函数表,不能认为子类继承了一个父类的虚函数表并且拥有一个自己的虚函数表。(但是多重继承情况下,子类可以拥有多个虚函数表)
-
如果子类中完全没有新的虚函数,则可以认为子类的虚函数表和父类的虚函数表内容相同,但这两个表在内存中是处于不同的位置的
-
虚函数表中的每一项保存着一个虚函数的地址,但如果子类的虚函数表某项和父类中的虚函数表某项代表同一个函数,则该表项所指向的该函数的地址应该相同
-
超出虚函数表部分的内存内容不可测
继续探索
#include <iostream>
using namespace std;
class Base
{
public:
virtual void f() { cout << "Base::f()" << endl; }
virtual void g() { cout << "Base::g()" << endl; }
virtual void h() { cout << "Base::h()" << endl; }
};
class Derive : public Base
{
public:
void g() { cout << "Derive::g()" << endl; }
};
int main()
{
typedef void(*Func)(void); // 定义了一个函数指针,typedef就代表Func是一个函数指针类型,可以将它当作类型使用
Derive derive;
long* pvptrderive = (long*)(&derive);
long* vptrderive = (long*)(*pvptrderive); // 虚函数表的地址
Derive derive2 = derive; // 复制构造
long* pvptrderive2 = (long*)(&derive2);
long* vptrderive2 = (long*)(*pvptrderive2);
// 父类指针指向子类
Base base = derive; // 直接用子类对象给父类对象值
long* pvptrbase = (long*)(&base);
long* vptrbase = (long*)(*pvptrbase);
printf("vptrderive:x0: %p\n", vptrderive); // 00E19B6C
printf("vptrbase:x0: %p\n", vptrderive2); // 00E19B6C
printf("vptrderive2:x0:%p\n", vptrbase); // 00E19B34
return 0;
}
vptrderive:x0: 00E19B6C
vptrbase:x0: 00E19B6C
vptrderive2:x0:00E19B34
- base