虚函数表
- 介绍
- 源码
- 运行结果
- 笔记扩充
- 函数名联编
- 静态联编
- 动态联编
介绍
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:俗称 函数指针)