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

c++————虚函数&虚函数表,第1张

c++————虚函数&虚函数表 C++虚函数 原理

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

虚函数指针

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

虚函数表的指针

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

虚函数表

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

例子一
#include 
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 
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);
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5711635.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存