c++————虚函数&虚函数表

C++虚函数

原理

C++中的虚函数的作用主要是实现了多态的机制。关于多态,就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。也就是试图使用不变的代码来实现可变的算法。虚函数的实现包括两部分,虚函数指针和虚函数表。

虚函数指针

虚函数指针 (virtual function pointer) 从本质上来说是一个指向函数的指针,与普通的指针并无区别。它指向用户所定义的虚函数,具体是在子类里的实现,当子类调用虚函数的时候,实际上是通过调用该虚函数指针从而找到接口。
虚函数指针是确实存在的数据类型,在一个被实例化的对象中,它总是被存放在该对象的地址首位,这种做法的目的是为了保证运行的快速性。与对象的成员不同,虚函数指针对外部是完全不可见的,除非通过直接访问地址的做法或者在DEBUG模式中,否则它是不可见的也不能被外界调用。
只有拥有虚函数的类才会拥有虚函数指针,每一个虚函数也都会对应一个虚函数指针。所以拥有虚函数的类的所有对象都会因为虚函数产生额外的开销,并且也会在一定程度上降低程序速度。

虚函数表的指针

虚函数表的指针,实质是指针的指针。虚函数表的内容其实就是一个指针数组。

虚函数表

虚函数表的指针存储在对象实例中最前面的位置。也就是说可以利用对象的实例地址得到虚函数表的地址,之后遍历虚函数表中的各个函数的指针,就可以调用与之对应的函数。

例子一

#include <iostream>
using namespace std;
class Base {//基类中的虚函数
public:
	virtual void f() { cout << "Base::f" << endl; }
	virtual void g() { cout << "Base::g" << endl; }
	virtual void h() { cout << "Base::h" << endl; }

};
int main()
{
	typedef void(*Fun)(void);//函数指针
	Base b;
	Fun pFun = NULL;
	Base* p = &b;
	cout << "该对象的地址:" << p << endl;
	cout << "虚函数表的指针" << (int*)(&b) << "开始存的" << endl << endl;
	cout << "虚函数表的指针指向的地址10进制:" << *(int*)(&b) << "即虚函数表的指针存的内容" << endl;
	cout << "即虚函数表的地址:" << (int*)*(int*)(&b) << endl << endl;
	pFun = (Fun) * (int*)*(int*)(&b);//第一个虚函数的指针
	cout << "第一个虚函数的地址:" << pFun << endl;
	pFun();
	Fun gFun = NULL;
	gFun = (Fun) * ((int*)*(int*)(&b) + 1);//第二个虚函数的指针
	Fun hFun = NULL;
	hFun = (Fun) * ((int*)*(int*)(&b) + 2);//第三个虚函数的指针
	return 0;
}

解释以下含义:
1、&b :对象b的地址
2、(int*)(&b): “虚函数指针vptr的地址”
3、*(int*)(&b):是虚函数表的地址;
4、 (int*)*(int*)(&b):虚函数表的地址转为int类型;
5、(Fun)*(int*)*(int*)(&b)虚函数表的int类型的地址再转为FUN函数指针类型
看一下 图
在这里插入图片描述那么gFun = (Fun)((int)(int)(&b) + 1);了。首先(int*)(int)(&b)将虚函数表的指针转换为指向指针数组首元素的指针(即转换过程中,指针指向地址没变的),然后((int*)(int)(&b) + 1)这里就是数组的指针的正常操作,现在这个指针指向了数组的第二个元素(即第二个虚函数指针),最后就是解引用,然后转换为Fun函数指针。

在这里插入图片描述

继承

#include <iostream>
using namespace std;
class Base1 {
public:
	virtual void f() { cout << "Base1::f" << endl; }
	virtual void g() { cout << "Base1::g" << endl; }
	virtual void h() { cout << "Base1::h" << endl; }
};
class Base2 {
public:
	virtual void f() { cout << "Base2::f" << endl; }
	virtual void g() { cout << "Base2::g" << endl; }
	virtual void h() { cout << "Base2::h" << endl; }
};
class Base3 {
public:
	virtual void f() { cout << "Base3::f" << endl; }
	virtual void g() { cout << "Base3::g" << endl; }
	virtual void h() { cout << "Base3::h" << endl; }
};
class Derive : public Base1, public Base2, public Base3 {
public:
	virtual void f() { cout << "Derive::f" << endl; }
	virtual void g1() { cout << "Derive::g" << endl; }
	virtual void h1() { cout << "Derive::h" << endl; }
};
typedef void(*Fun)(void);
void printVfun(int n, int * vTable) {
	for (int i = 0; i < n; ++i)
	{
		printf("function : %d :0X%x->", i, vTable[i]);
		Fun f = (Fun)(vTable[i]);
		f();         //访问虚函数
	}
	cout << "" << endl;
}
int main()
{
	Derive d;
//  (int*)(&b)这个是虚函数表指针vptr的地址
// *(int*)(&b)这个是虚函数表的地址
// 一个对象中有多个虚函数表时需要在 第二个虚函数表指针vptr:(int*)(&b)+1
	int *vTable1 = (int *)*(int *)(&d);//第一个虚函数表的指针
	printVfun(5, vTable1);

	int *vTable2 = (int *)*((int *)(&d) + 1);//第二个虚函数表的指针
	printVfun(3, vTable2);

	int *vTable3 = (int *)*((int *)(&d) + 2);//第三个虚函数表的指针
	printVfun(3, vTable3);
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值