C++_虚函数表

介绍

1.编译器通过指针或引用调用虚函数,不会立即生成函数调用指令,而是用 二级函数指针 代替

1.1确定真实类型
1.2找到虚函数表从而找到入口地址
1.3根据入口地址调用函数(PS:俗称 函数指针)

** 虚函数表 内部存储格式展示**
在这里插入图片描述
代码图详解
在这里插入图片描述

源码

#include<iostream>
#include<string>
using namespace std;

class N
{
public:
	void foo()
	{
		cout << "N::foo" << endl;
	}
	void ber()
	{
		cout << "N::ber" << endl;
	}
	int m_a;
	int m_b;
};
class A
{
public:
	virtual void foo()
	{
		cout << "A::foo" << endl;
	}
	virtual void ber()
	{
		cout << "A::ber" << endl;
	}
	double m_a;
	int m_b;
};
class B:public A
{
public:
	void foo()
	{
		cout << "B::foo" << endl;
	}
	void ber()
	{
		cout << "B::ber" << endl;
	}
};
void main()
{
	A a;//在A类型中会多生成一个最大类型的 字节指针(内部存储的是函数指针)
	B b;//在B类型中会多生成一个最大类型的 字节指针(内部存储的是函数指针)
	A*pa = &b;

	//获取 N 类型大小
	cout << "sizeof(N):" << sizeof(N);
	//获取 N 相对于 m_a 的距离
	cout << ",m_a:" << offsetof(N, m_a);
	//获取 N 相对于 m_b 的距离
	cout << ",m_b:" << offsetof(N, m_b) << endl;

	//获取 A 类型大小
	cout << "sizeof(N):" << sizeof(A);
	//获取 A 相对于 m_a 的距离
	cout << ",m_a:" << offsetof(A, m_a);
	//获取 A 相对于 m_b 的距离
	cout << ",m_b:" << offsetof(A, m_b) << endl;
	cout << "-------------------分隔符-------------------" << endl;
//函数指针调用函数
	
	void *vf_ptr = *(void**)&a; //获取 A类型 中 a 地址
	cout <<"A类型 中 a 地址为:"<< vf_ptr << endl;    //打印 A类型 中 a 地址
	cout << "-------------------分隔符-------------------" << endl;
	//取别名
	typedef void(*VFUN)(void*); //VFUN 相当于 void(*)  // *VFUN 函数指针类型
	typedef VFUN* VPTR;         //VPTR 相当于 void(**) //指向函数指针类型的指针 VPTR虚函数表类型
	VPTR _vfptr = *(VPTR*)&a;   //获取 A类型 中 a 地址
	cout << "A类型 中 a 地址为:" << _vfptr << endl;   //打印 A类型 中 a 地址
	a.foo();                    //正常调用方法
	_vfptr[0](&a);              //编译器中调用方法 结果为:A::foo
	_vfptr[1](&a);              //编译器中调用方法 结果为:A::ber

	cout << "-------------------分隔符-------------------" << endl;
	VPTR _vfptr1 = *(VPTR*)&b;   //获取 B类型 中 b 地址
	cout << "B类型 中 b 地址为:" << _vfptr1 << endl;   //打印 B类型 中 b 地址
	b.foo();                    //正常调用方法
	_vfptr1[0](&b);              //编译器中调用方法 结果为:B::foo
	_vfptr1[1](&b);              //编译器中调用方法 结果为:B::ber

	system("pause");
}

运行结果

sizeof(N):8,m_a:0,m_b:4
sizeof(N):24,m_a:8,m_b:16
-------------------分隔符-------------------
A类型 中 a 地址为:0015DC80
-------------------分隔符-------------------
A类型 中 a 地址为:0015DC80
A::foo
A::foo
A::ber
-------------------分隔符-------------------
B类型 中 b 地址为:0015DCA0
B::foo
B::foo
B::ber
请按任意键继续. . .

笔记扩充

void(VFUN)(void) 的代码解释,参考下列源码

#include<iostream>
#include<string>
using namespace std;

//构造3个通用函数
void TEST1(void) { printf("test1\n"); }//函数定义  
void TEST2(void) { printf("test2\n"); }//函数定义  
void TEST3(void) { printf("test3\n"); }//函数定义  
//声明(取别名为:(*func)函数)是 void 类型
typedef void(*func)(void);

void test(int i)
{
	func vTask[3] = { &TEST1, &TEST2, &TEST3 };//func 类型代替 void功能
	func fun = vTask[i];//将vTask函数赋值到fun中
	(*fun)();//调用(*fun) ()函数
}
void main()
{
	test(0);
	test(1);
	test(2);
	system("pause");
}

函数名联编

将源代码中的函数调用解释为执行特定的函数代码块被称为 函数名联编

静态联编

1.静态联编是指 联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的, 又称为:早期联编
2.静态联编对函数的选择是基于指向对象的指针或者引用的类型
3.优点是效率高,灵活性差
4.静态联编是根据 所定义的类 来调用 类中函数(PS:相当于直接调用当前类代码,不会做任何检查)

动态联编

1.动态联编 是指联编在程序运行时动态地进行,这种联编又称为晚期联编。或动态束定
2.动态联编对成员函数的选择是基于对象的类型
3.优点是灵活性强,效率低
4.实际上是在运行时虚函数(virtual)的实现。(PS:先进行检查后,根据当时的情况来确定调用哪个同名函数)
动态联编 调用步骤:

1.1确定真实类型
1.2找到虚函数表从而找到入口地址
1.3根据入口地址调用函数(PS:俗称 函数指针)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值