虛指針與虛表
虛表和虛函數是為了實現動態(tài)多態(tài)的機制,由編譯器實現
當一個類本身定義了虛函數哼蛆,或其父類有虛函數時亏吝,編譯器為該類創(chuàng)建一個虛函數表,表中按函數定義的順序存放指向虛函數的指針脆丁。
帶虛函數的類實例化為對象的時候,在堆棧上給對象的開始位置加一個虛函數指針动雹,(放在開始是為了保證在多層繼承或多重繼承的情況下能以最高)
函數表vtable在Linux/Unix中存放在可執(zhí)行文件的只讀數據段中(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字節(jié)的(64/8)静暂,所以用long,在32位機器上谱秽,地址是4字節(jié)
運行結果:
又學到一個牛逼的方法
用long** pVtab= (long**)&a;
來操作
此時pVtab[0]就是虛函數表地址洽蛀,pVtab[1]依次往下就是成員變量,注意內存對齊弯院,如果成員有連續(xù)兩個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指針指向的地址==對象的首地址(對象的位置)==虛指針的位置