多重继承下的Virtual Function 内存布局

本文分析了C++中多重继承下虚函数表的内存布局,包括派生类虚函数表的构成、重写和新增虚函数的位置,以及VS2010编译器的实现细节。通过实例展示了虚函数表的分布和对象内存布局,并讨论了不同基类指针调用虚函数时的机制。
摘要由CSDN通过智能技术生成

本文的例子来自<<Inside The C++ Object Model>> P159 ,转发请注明出处,谢谢。

1.先给出测试代码和测试结果

#include<iostream>
using namespace std;
class Base1
{
public:
	virtual ~Base1() {};
	virtual void speakClearly() {cout<<"Base1::speakClearly()"<<endl;}
	virtual Base1 *clone() const {cout<<"Base1::clone()"<<endl; return new Base1;}
protected:
	float data_Base1;
};
class Base2
{
public:
	virtual ~Base2() {};
	virtual void mumble() {cout<<"Base2::mumble()"<<endl;}
	virtual Base2 *clone() const {cout<<"Base2::clone()"<<endl; return new Base2;}
protected:
	float data_Base2;
};
class Derived : public Base1,public Base2
{
public:
	virtual ~Derived()  {cout<<"Derived::~Derived()"<<endl;}
	virtual Derived *clone() const {cout<<"Derived::clone()"<<endl; return new Derived;}
protected:
	float data_Derived;
};
int main()
{
	cout<<sizeof(Base1)<<endl;
	typedef void (*Fun) ();
	Fun pFun = NULL;
	//******************************************************//
	  // vitrual table[0]
	//******************************************************//
	cout<<"################ test virtual table[0]   ################"<<endl<<endl;
    Derived d;
	cout<<"&d = "<<&d<<endl;
	
	int *vptr1 = (int*)*((int*)&d+0);  //vptr1为virutal table[0]的地址
	cout<<"vptr1 = "<<vptr1<<endl;
	int *pf1 = (int*)*((int*)*((int*)&d+0)+0);   //pf1为virtual table[0]里的第一个虚拟函数Derived::~Derived()的地址
  //int *pf1 = (int*)*((int*)vptr1[0]);  //与上面等价
	cout<<"&vptr1[0] = "<<&vptr1[0]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pf1 = (int*)*((int*)*((int*)&d+0)+1) ;//pf1为virtual table[0]里的第二个虚拟函数Base1::speakClearly()的地址
	cout<<"&vptr1[1] = "<<&vptr1[1]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pFun = (Fun)pf1;                       
	pFun();
	pf1 = (int*)*((int*)*((int*)&d+0)+2) ;//pf1为virtual table[0]里的第三个虚拟函数Derived::clone()的地址
	cout<<"&vptr1[2] = "<<&vptr1[2]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pFun = (Fun)pf1;
	pFun();
	pf1 = (int*)*((int*)*((int*)&d+0)+3) ;
	cout<<"&vptr1[3] = "<<&vptr1[3]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	if(pf1 == NULL)
		cout<<"NUll"<<endl;           //结果表明,virtual table[0][3]为NULL
	//******************************************************//
	  // vitrual table[1]
	//******************************************************//
	cout<<endl<<endl<<"################ test virtual table[1]   ################"<<endl<<endl;
	int sz = sizeof(Base1)/4;
	int *vptr2 = (int*)*((int*)&d+sz);  //vptr2为virutal table[1]的地址
	cout<<"vptr2 = "<<vptr2<<endl;
	pf1 = (int*)*((int*)*((int*)&d+sz)+0); //pf1为virtual table[1]里的第一个虚拟函数Derived::~Derived()的地址
	cout<<"&vptr2[0] = "<<&vptr2[0]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pf1 = (int*)*((int*)*((int*)&d+sz)+1); //pf1为virtual table[1]里的第二个虚拟函数Based::mumble()的地址
	cout<<"&vptr2[1] = "<<&vptr2[1]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pFun = (Fun)pf1;
	pFun();
	pf1 = (int*)*((int*)*((int*)&d+sz)+2); //pf1为virtual table[1]里的第三个虚拟函数Derived::clone()的地址
	cout<<"&vptr2[2] = "<<&vptr2[2]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	pFun = (Fun)pf1;
	pFun();
	pf1 = (int*)*((int*)*((int*)&d+sz)+3) ;
	cout<<"&vptr2[3] = "<<&vptr2[3]<<endl;  //??????
	cout<<"pf1 = "<<pf1<<endl;
	if(pf1 == NULL)
		cout<<"NUll"<<endl;         
	pFun = (Fun)pf1;   //  虚函数表里问什么有两个地址指向Derived::clone()
	pFun();
	pf1 = (int*)*((int*)*((int*)&d+sz)+4) ;
	cout<<"&vptr2[4] = "<<&vptr2[4]<<endl;
	cout<<"pf1 = "<<pf1<<endl;
	if(pf1 == NULL)
		cout<<"NUll"<<endl;           //结果表明,virtual table[0][3]为NULL
	return 0;
}

用VS2010测试的结果:

结果分析:

 

2.总结

   1).派生类的虚函数表数目是它所有基类的虚函数数目之和,基类的虚函数表被复制到派生类的对应的虚函数表中。

   2).派生类中重写基类的虚拟函数时,该被重写的函数在派生类的虚函数列表中得到更新,派生类的虚析构函数覆盖基类的虚析构函数。

   3).派生类中新增加的虚函数被添加到与第一个基类相对应的虚函数表中。

   4).测试表明:在VS2010的C++编译器中,虚函数表不总是以NULL结束。

   5).virtual table[1]中的clone分别为:Base2* Derived::clone 和 Derived* Derived::clone 。这里为什么会比table[0]多一个两个Base2* Derived::clone呢?因为:如果将一个Derived对象地址指定给一个Base1指针或者Derived指针是,虚拟机制使用的是virtual table[0] ;如果将一个Derived对象地址指定给一个Base2指针时,虚拟机制使用的是virtual table[1]。 (<<C++对象模型>> P164)

通过命令行 /d1reportSingleClassLayoutDerived 可以看到Derived的内存布局:

 1>  class Derived size(20):
1>   +---
1>   | +--- (base class Base1)
1>   0 | | {vfptr}
1>   4 | | data_Base1
1>   | +---
1>   | +--- (base class Base2)
1>   8 | | {vfptr}
1>  12 | | data_Base2
1>   | +---
1>  16 | data_Derived
1>   +---
1>  
1>  Derived::$vftable@Base1@:
1>   | &Derived_meta
1>   |  0
1>   0 | &Derived::{dtor}
1>   1 | &Base1::speakClearly
1>   2 | &Derived::clone
1>  
1>  Derived::$vftable@Base2@:
1>   | -8
1>   0 | &thunk: this-=8; goto Derived::{dtor}
1>   1 | &Base2::mumble
1>   2 | &thunk: this-=8; goto Base2* Derived::clone
1>   3 | &thunk: this-=8; goto Derived* Derived::clone

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值