虚指针与虚表
-
虚表和虚函数是为了实现动态多态的机制,由编译器实现
-
当一个类本身定义了虚函数,或其父类有虚函数时,编译器为该类创建一个虚函数表,表中按函数定义的顺序存放指向虚函数的指针。
-
带虚函数的类实例化为对象的时候,在堆栈上给对象的开始位置加一个虚函数指针,(放在开始是为了保证在多层继承或多重继承的情况下能以最高)
-
函数表vtable在Linux/Unix中存放在可执行文件的只读数据段中(rodata),这与微软的编译器将虚函数表存放在常量段存在一些差别
-
虚函数表是class specific的,也就是针对一个类来说的,这里有点像一个类里面的staic成员变量,即它是属于一个类所有对象的,不是属于某一个对象特有的,是一个类所有对象共有的。
代码:通过函数指针访问类对象中的虚函数表,来调用虚表中的虚函数
- 对象的地址就是存放虚指针的地址,
long * vptrAddress = (long *)(&a);
- 虚指针指向虚表,
long* vfunAddress = (long*)*vptrAddress;
- 虚表中存放虚函数的地址,用函数指针取函数,
vfunc = (F)*(long*)vfunAddress;
#include <iostream>
using namespace std;
class A
{
public:
virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
};
typedef void (*F)();
F vfunc;//定义和声明函数指针
int main()
{
A a;
//获取对象首地址(即虚表指针)
long * vptrAddress = (long *)(&a);
cout << vptrAddress<<endl;
//获取虚表的首地址
long* vfunAddress = (long*)*vptrAddress;
cout << vfunAddress<<endl;
//用函数指针直接调用地址的方法调用类中的函数
// vfunc = (F)*((long*)*(long*)(&a));
vfunc = (F)*(long*)vfunAddress;
vfunc();
//指针指向虚表下一个函数的地址,赋给函数指针来进行调用
vfunc = (F)*((long*)vfunAddress+1);
vfunc();
return 0;
}
注意地址要用long *来存,不然存不下,指向不知道什么地方造成段错误
因为64位机器上,指针是8字节的(64/8),所以用long,在32位机器上,地址是4字节
运行结果:
又学到一个牛逼的方法
用long** pVtab= (long**)&a;
来操作
此时pVtab[0]就是虚函数表地址,pVtab[1]依次往下就是成员变量,注意内存对齐,如果成员有连续两个int,取到的就是一个不想要的值
#include <iostream>
using namespace std;
class A
{
public:
virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
A(int MEM,int MEM1)
:mem(MEM),mem1(MEM1){}
public:
int mem;
long mem1;
};
typedef void (*F)(void);
F vfunc;//定义和声明函数指针
A a(10,20);
long** pVtab = (long**)&a;
int main()
{
vfunc = (F)pVtab[0][0];
vfunc();
vfunc = (F)pVtab[0][1];
vfunc();
//access mem
cout << "mem="<<(long)pVtab[1]<<endl;
cout << "mem1="<<(long)pVtab[2]<<endl;
return 0;
}
今天学习this指针的时候又在原程序上加了些功能
#include <iostream>
using namespace std;
class A
{
public:
virtual void Fun(){cout<<"this is A::Fun()"<<endl;}
virtual void Fun1(){cout<<"this is A::Fun1()"<<endl;}
A(int MEM,int MEM1)
:mem(MEM),mem1(MEM1){}
void showThis(){cout<<"this pointer: " << "\t"<< hex << this << endl;}
public:
int mem;
long mem1;
};
typedef void (*F)(void);
F vfunc;//定义和声明函数指针
A a(10,20);
long** pVtab = (long**)&a;
int main()
{
//access function member
vfunc = (F)pVtab[0][0];
vfunc();
vfunc = (F)pVtab[0][1];
vfunc();
//access data member
cout << "mem="<< (long)pVtab[1] << endl;
cout << "mem1="<< (long)pVtab[2] << endl;
cout << "addr of vp: " << "\t" << hex << &pVtab[0] << endl;
cout << "addr of a: " << "\t" << hex << &a << endl;
a.showThis();
return 0;
}
可见,this指针指向的地址==对象的首地址(对象的位置)==虚指针的位置
网友评论