为什么有虚函数表?
主要还是为了实现C++的多态,即运行时绑定,而且虚函数表是在编译时期就已确认,后续链接、执行时都不会改变其地址
示例
#include <iostream>
using namespace std;
class Base
{
public:
virtual void a();
virtual void b();
virtual void c();
};
void Base::a()
{
cout << "base::a" << endl;
}
void Base::b()
{
cout << "base::b" << endl;
}
void Base::c()
{
cout << "base::c" << endl;
}
class Derived : public Base
{
public:
void a();
};
void Derived::a()
{
cout << "derived::a" << endl;
}
typedef void (*func)();
int main(int argc, char **argv)
{
// 先看继承类的地址空间
Derived derive;
printf("derive::vptr %p\n", *(int *)&derive);
// 获取虚函数表的地址
int *v_ptr = (int *)(*(int *)&derive);
printf("derive::vptr->a %p\n", *v_ptr);
printf("derive::vptr->b %p\n", *(v_ptr+1));
printf("derive::vptr->c %p\n", *(v_ptr+2));
((func)*v_ptr)();
((func)*(v_ptr + 1))();
((func)*(v_ptr + 2))();
// 看看Base的地址空间
Base base1;
printf("base1::vptr %p\n", *(int *)&base1);
// 获取虚函数表的地址
v_ptr = (int *)(*(int *)&base1);
printf("base1::vptr->a %p\n", *v_ptr);
printf("base1::vptr->b %p\n", *(v_ptr + 1));
printf("base1::vptr->c %p\n", *(v_ptr + 2));
((func)*v_ptr)();
((func)*(v_ptr + 1))();
((func)*(v_ptr + 2))();
// 看看Base的地址空间
Base base2;
printf("base2::vptr %p\n", *(int *)&base2);
// 获取虚函数表的地址
v_ptr = (int *)(*(int *)&base2);
printf("base2::vptr->a %p\n", *v_ptr);
printf("base2::vptr->b %p\n", *(v_ptr + 1));
printf("base2::vptr->c %p\n", *(v_ptr + 2));
((func)*v_ptr)();
((func)*(v_ptr + 1))();
((func)*(v_ptr + 2))();
int test;
cin >> test;
return 0;
}
输出:
derive:vptr 00268B6C
derive:vptr->a 002610A0
derive:vptr->b 00261415
derive:vptr->c 002613D4
derived::a
base::b
base::c
base1:vptr 00268B34
base1:vptr->a 002610E6
base1:vptr->b 00261415
base1:vptr->c 002613D4
base::a
base::b
base::c
base2:vptr 00268B34
base2:vptr->a 002610E6
base2:vptr->b 00261415
base2:vptr->c 002613D4
base::a
base::b
base::c
结论:
- 虚函数和类相关,和对象无关,从base1和base2的虚函数的地址完全一样可知
- 继承关系时,derived重写的虚函数,会将对应虚函数表中的函数地址改写成子类的函数的地址,以实现动态binding
网友评论