编译器在编译的时候,发现基类中有虚函数,此时编译器会为每个包含虚函数的类创建一个虚表(vtable),该表是一个一维数组,在这个数组中存放每个虚函数地址,这张表解决了继承、覆盖的问题,保证其真实放映实际的函数。编译器会为基类与继承类都建立一个虚表(即便继承类中没有virtual函数,但是基类中有,那么继承类也就是有)。一般编译器将指向虚表指针存放在实例对象中第一个地址。那么其实我们只要获得这个类的实例对象的地址,就获得了指向该虚表的指针的地址。如图:
举例分析:
首先验证是否实例化的第一个地址是虚表,输出虚表第一个函数。
class base { public: vritual void first( ); virtual void second( ); }; void base::first( ) { std::cout<<"In base the first virtual function "<<std::endl; } void base::second( ) { std::cout<<"In base the second virtual function "<<std::endl; } int main(int argc,char ** argv) { typedef void (*fun)(void); fun f=NULL; base b; std::cout<<"vtptr address is "<<&b<<endl; //这里b地址直接取的话,就是object了,需要通过类型转换获得其 //第一个地址,然后取该地址所指向的内容,在将其类容转换成地址 std::cout<<"vt address is "<<(int *)*(int *)&b<<std::endl; //取虚表中的第一个地址 f=(fun)*(int *)*(int *)&b; f( ); f=(fun)*( (int *)*(int *)&b + 1 ); f( ); std::cout<<"the class instance size is "<<sizeof(b)<<std::endl; return 0; } |
运行结果
通过这段代码可以肯定结构如果所示,因为该对象的大小就是4,所以只有一个指针。