C++虚表的实现原理(windows & linux上)

当大家被问到C++多态是怎么实现的时候,一般也就想到了虚函数吧,进一步也就是想到了与其关联的虚表指针,那我们就该想想这个虚表到底是怎么做到的呢。

/*author:  Jeson Yang
 date:	2015.11.21
 file:	****.cpp*/

#include 
using namespace std;

#define __stdcall
class CBase
{
public:
	virtual void f(){ cout << "f()" << endl; };
	virtual void g(){ cout << "g()" << endl; };
	virtual int i(int nNum){ cout << "i() - " << nNum << endl; return 0; };
	void m(){ cout << "m()" << endl; };		//混淆视听的非虚函数
	virtual void h(){ cout << "h()" << endl; };
};

int main()
{
	CBase cb;
	typedef void(__stdcall *pFun)();
	typedef int(__stdcall *pFun1)(int);		//告诉编译器使用远的C函数吧,不加可能出错喔			

	pFun f;
	pFun1 f1;
	int *vPtr = (int*)&cb;		//虚表指针喽
	int *vFun = (int*)((int*)*vPtr + 1);	//通过虚表找到的 指向第二个虚函数地址的指针
	f = (pFun)*vFun;	//通过虚表找到的 第二个虚函数地址
	//f = (pFun)*(int*)((int*)*vPtr + 1);
	//f = (pFun)*(int*)((int*)*(int*)&cb + 1);
	f();	//打印g()
	
	f1 = (pFun1)*(int*)((int*)*vPtr + 2);	//跟上面的差不多吧
	f1(100);	//打印i() -100

	f = (pFun)*(int*)((int*)*vPtr + 3);	//函数m在前面,但是不是虚函数,所以跳过喽
	f();	//打印h()

	//关于越界后面改成+4(算是结束点吧) windows7+vs2013为NULL
	//linux据说有些环境f值为1表示有下一个虚表,0表示结束吧待考证
	//本人linux好像结果不一致

return 0;
}
具体根据这个注释走一遍,关于这种最简单结构的虚表的结构应该能画出来吧。

 

 

如果以上能完全理解了,下面就可以看看有多重继承关系的结构了,个人对于画图不怎么在行,也就只好通过注释来理解了,不便见谅。

 
/*author:  Jeson Yang 
 date:  2015.11.21 
 file:  ****.cpp*/  
  
#include   
using namespace std;  
  
#define __stdcall  
class CBase  
{  
public:  
    virtual void f(){ cout << "f()" << endl; };  
    virtual void g(){ cout << "g()" << endl; };  
    virtual int i(int nNum){ cout << "i() - " << nNum << endl; return 0; };  
    void m(){ cout << "m()" << endl; };     //混淆视听的非虚函数  
    virtual void h(){ cout << "h()" << endl; };  
    //int m_nNum;       //linux上可以打开  
};  
  
class CBase1  
{  
public:  
    virtual void f(){ cout << "f1()" << endl; };  
};  
  
class cDerive : public CBase,public CBase1  
{  
public:  
    void f(){ cout << "derive f()" << endl; };      //自动虚,覆盖基类的f()  
private:  
  
};  
  
int main()  
{  
    cDerive cb;  
    typedef void(__stdcall *pFun)();  
    typedef int(__stdcall *pFun1)(int);     //告诉编译器使用远的C函数吧,不加可能出错喔  
                  
    pFun f;  
    int **vPtr = (int**)&cb;      
  
    cout << sizeof(cDerive) << endl;  
    f = (pFun)vPtr[0][0];   //通过虚表找到的 指向第一个虚函数地址的指针  
    f = (pFun)*(int*)(*(int*)((int*)&cb + 0) + 0);  
    //f = (pFun)(int*)&cb;  
    f();    //打印f1(),多态的实现,函数指针的覆盖  
      
    f = (pFun)*(int*)(*(int*)((char*)&cb + sizeof(CBase)) + 0);   
    //此处写法为了适应linux(centos5.6 g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-55))上  
    //打开CBase里面的int参数  
    //windows上并不适用  
  
    f();    //打印f1(),多态的实现,函数指针的覆盖  
  
    return 0;  
}  

 
  
 
  

当然虚表实现也有些不足,假如将以上f()设置private 以上依旧能够访问,这也是十分危险的,故windows linux上都通过

windows上应该加了些保护机制吧,对于基类中有一些其他成员变量的好像不能通过指针计算偏移的方式访问到吧,如果能够做到或者有什么错误希望能留言联系吧

 

 

 

你可能感兴趣的:(C++)