设为首页 加入收藏

TOP

C++拾遗--虚函数表
2015-07-20 17:15:59 来源: 作者: 【 】 浏览:2
Tags:拾遗 函数

C++拾遗--虚函数表

前言

C++的多态依赖虚函数来实现。若类存在虚函数,则每一个类的实例都维护了一个地址,这个地址指向虚函数表。虚函数表中存放的是类中所有虚函数的地址。下面我们找出虚函数表的地址,从而获得每个虚函数的地址,然后使用地址直接调用虚函数。

正文

1.空类的size

?

#include 
  
   
using namespace std;
class MyClass
{
	
};
int main()
{
	cout << sizeof(MyClass) =  << sizeof(MyClass) << endl;
	cin.get();
	return 0;
}
  
运行

?

\

空类没有定义任何的成员,这样的类size都是1,1表示该类存在。

?

2.有函数(非虚函数)成员的类

?

#include 
  
   
using namespace std;
class MyClass
{
	void fun(){};
};
int main()
{
	cout <<  sizeof(MyClass) = << sizeof(MyClass) << endl;
	cin.get();
	return 0;
}
  
运行

?

\

这样的类:无数据成员,只有非虚的函数成员,它的size都是1。原因:代码区不计入size、

?

3.只含有虚函数的类

?

#include 
  
   
using namespace std;
class MyClass
{
	virtual void fun(){};
};
int main()
{
	cout <<  sizeof(MyClass) = << sizeof(MyClass) << endl;
	cin.get();
	return 0;
}
  
运行

?

\

这个4即是指向虚函数表指针的大小。

?

4.虚函数表

无论该类是否有数据成员,指向虚函数表的指针,都位于对象的首地址处。

?

#include 
  
   
using namespace std;

class MyClass
{
public:
	virtual void fun1()
	{
		cout << void fun1() << endl;
	}
	virtual void fun2()
	{
		cout << void fun2() << endl;
	}
	virtual void fun3()
	{
		cout << void fun3() << endl;
	}
};
//Fun是函数指针
typedef void(*Fun)();
int main()
{
	cout << ******虚函数原理***by David*** << endl;
	MyClass *p = new MyClass;
	cout << 虚函数表的地址 << ends;
	cout << (void*)*(int*)p << endl;
	Fun pfun1 = (Fun)*(int*)(*(int*)p);
	cout << (void*)pfun1 << ends;
	pfun1();
	Fun pfun2 = (Fun)*((int*)(*(int*)p) + 1);
	cout << (void*)pfun2 << ends;
	pfun2();
	Fun pfun3 = (Fun)*((int*)(*(int*)p) + 2);
	cout << (void*)pfun3 << ends;
	pfun3();
	//断点
	cin.get();
	return 0;
}
  

运行

\

在调试窗口中可看到相关局部变量和虚函数表的相关信息

\
?

下面来详细分析下,我们是如何获得虚函数表的地址的

\

联合上图,我们来一步一步推导出虚函数表的地址以及各个虚函数的地址

1.指针p的类型是MyClass *

2.虚函数表的地址在对象的首部(前四个字节),*p的类型是MyClass,为了获取前四个字节的存储内容,必须对p进行类型转换->(int*)p

3.如是*(int*)p就是虚函数表的地址,之所以加上void*,因为这是C++中对指针的标准打印方式。

4.虚函数表的地址*(int*)p,指向虚函数表的首部。每个虚函数的地址都是四个字节大小的,也就是说,虚函数表的前四个字节的存储内容就是第一个虚函数fun1的地址。随后四个字节的存储内容是第二个虚函数fun2的地址,以此类推……

5.为了获取fun1的地址,同理需要对*(int*)p进行类型转换->(int*)(*(int*)p),需要指出此时*(int*)p的类型是int*,解引用后就是fun1的地址了 *(int*)(*(int*)p)

6.如何获取fun2的地址呢,那当然就是对上一步的指针(int*)(*(int*)p)进行移动,移动4个字节 (int*)(*(int*)p)+1,解引用后*((int*)(*(int*)p)+1),就是fun2的地址。再次移动四个字节 (int*)(*(int*)p)+2,解引用后*((int*)(*(int*)p)+2),就是fun3的地址。

?

?

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇POJ 1440-Varacious Steve(DP) 下一篇c++ 泛型编程 之 TypeLists

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Redis on AWS:Elast (2025-12-27 04:19:30)
·在 Spring Boot 项目 (2025-12-27 04:19:27)
·使用华为开发者空间 (2025-12-27 04:19:24)
·Getting Started wit (2025-12-27 03:49:24)
·Ubuntu 上最好用的中 (2025-12-27 03:49:20)