每日一记----虚函数表

虚函数表的一些用法

引言:虚函数对于学习C++的同行来说一点都不陌生,是实现动多态(何为静多态)的基本手段,那么编译器又是如何通过虚函数来完成所需函数的调用呢?接下来按照几个例子进行一下实验。

1、开胃菜,小例子,大视野

class Base
{
public:
	virtual void Print(){cout<<"This is Base"<<endl;}
	int n;
	//Base(int a):n(a){};
	
};

class Derived: public Base
{
public:
	void Print(){cout<<"This is Derived"<<endl;}
	//void Print2(){cout<<"This is Derived2"<<endl;}
	//Derived(int a):Base(a){};

};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
	Base *pb = new Derived();
	//pb->Print();
	//cout<<&(pb->n)<<endl<<pb;
	//pb->Out();    //无法被调用
	int* pA = (int*)(*((int*)pb ));
	Func pFunc = (Func)*pA;
	pFunc();
	return 0;
}
2、C++是如何通过虚函数机制实现多态的


上述函数输出为 “This is Derived”,说明Derived类的Print函数被调用了。那么他是如何被调用的呢,首先要引入“虚函数表”的概念,如果一个类拥有virtural函数,那么C++会位每一个该类的实例分配一个虚函数表指针,该指针指向该类的虚函数表结构。用一张图来分析下

如上为父类Base的虚函数表结构图,(例图仅供参考了),那么子类的虚函数表又是什么样子的呢

我们会发现Base::f()被Derive::f()覆盖了,这是因为子类对父类的f()函数进行类override,所以子类的函数覆盖了父类的函数,按照上面的逻辑,那么当定义个父类的指针指向一个子类的对象,那么指针就会通过虚函数表读取需要调用的函数,因为虚函数表中只存在子类函数所以调用的是子类函数(当子类override父类的相同函数时)。通过如上机制,多态被实现出来了,有点意思浩。

2、虚函数指针的位置

上面的例子因为类中只有一个虚函数,没有其他成员变量,所以类占用的空间就是虚函数指针的空间(4个字节32位)。但是当存在成员变量是是如何分布的呢?

class Base
{
public:
	virtual void Print(){cout<<"This is Base"<<endl;}
	int n;
	Base(int a):n(a){};
	
};

class Derived: public Base
{
public:
	void Print(){cout<<"This is Derived"<<endl;}
	//void Print2(){cout<<"This is Derived2"<<endl;}
	Derived(int a):Base(a){};

};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
	Base *pb = new Derived(3);
	//pb->Print();
	cout<<&(pb->n)<<endl<<pb;
	//pb->Out();    //无法被调用
	int* pA = (int*)(*((int*)pb ));
	Func pFunc = (Func)*pA;
	//pFunc();
	return 0;
}
输出:

由上面输出可看出n的地址与指针pb指向实例地址偏移了4个字节,这4个自己是什么呢由开始那个例子可以看出占用的这个4个字节正是“虚函数指针”占用的地址。

3、多重继承时的情况

上面讲了有成员变量时候内存分布,但是当多重继承时,内存又是如何分布的呢


<pre name="code" class="cpp">class Base2
{
public:
	virtual void Print2(){cout<<"This is Base2"<<endl;};

};
class Base
{
public:
	virtual void Print(){cout<<"This is Base"<<endl;}
	int n;
	Base(int a):n(a){};
	
};

class Derived: public Base, public Base2
{
public:
	void Print(){cout<<"This is Derived"<<endl;}
	//void Print2(){cout<<"This is Derived2"<<endl;}
	Derived(int a):Base(a){};
	

};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
	Base2 *pb = new Derived(3);
	//pb->Print();
	//cout<<&(pb->n)<<endl<<pb;
	//pb->Out();    //无法被调用
	int* pA = (int*)(*((int*)pb));
	
	Func pFunc = (Func)*pA;
	pFunc();
	int* pB = (int*)(*((int*)pb - 2));
	pFunc = (Func)*pB;
	pFunc();
	return 0;
}


 输出: 

This is Base2 

This is Derived

 






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值