详解C++虚函数的工作原理

2020-06-22 13:09 来源:易采站长站 作者:丽君 点击: 评论:

A-A+

原标题:详解C++虚函数的工作原理

静态绑定与动态绑定

讨论静态绑定与动态绑定,首先需要理解的是绑定,何为绑定?函数调用与函数本身的关联,以及成员访问与变量内存地址间的关系,称为绑定。 理解了绑定后再理解静态与动态。

静态绑定:指在程序编译过程中,把函数调用与响应调用所需的代码结合的过程,称为静态绑定。发生在编译期。 动态绑定:指在执行期间判断所引用对象的实际类型,根据实际的类型调用其相应的方法。程序运行过程中,把函数调用与响应调用所需的代码相结合的过程称为动态绑定。发生于运行期。

C++中动态绑定

在C++中动态绑定是通过虚函数实现的,是多态实现的具体形式。而虚函数是通过虚函数表实现的。这个表中记录了虚函数的地址,解决继承、覆盖的问题,保证动态绑定时能够根据对象的实际类型调用正确的函数。这个虚函数表在什么地方呢?C++标准规格说明书中说到,编译器必须要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。也就是说,我们可以通过对象实例的地址得到这张虚函数表,然后可以遍历其中的函数指针,并调用相应的函数。

虚函数的工作原理

要想弄明白动态绑定,就必须弄懂虚函数的工作原理。C++中虚函数的实现一般是通过虚函数表实现的(C++规范中没有规定具体用哪种方法,但大部分的编译器厂商都选择此方法)。类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。编译器会为每个有虚函数的类创建一个虚函数表,该虚函数表将被该类的所有对象共享。 类的每个虚成员占据虚函数表中的一行。如果类中有N个虚函数,那么其虚函数表将有N*4字节的大小。

虚函数(virtual)是通过虚函数表来实现的,在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反映实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存(位于对象实例的最前面),所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要,指明了实际所应调用的函数。它是如何指明的呢?后面会讲到。

JMP指令是汇编语言中的无条件跳转指令,无条件跳转指令可转到内存中任何程序段。转移地址可在指令中给出,也可以在寄存器中给出,或在储存器中指出。

首先我们定义一个带有虚函数的基类

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!\n";
	}
	virtual void fun2(){
		cout<<"base fun2!\n";
	}
	virtual void fun3(){
		cout<<"base fun3!\n";
	}

	int a;
};

【易采站长站编辑:秋军】