东莞网站建设哪里找,闵行网站搭建哪里有,大连设计工作室,衡水做网站哪家好C 中的虚函数的作用主要是实现了多态的机制。关于多态#xff0c;简而言之就是用父类型别的指针指向其子类的实例#xff0c;然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”#xff0c;这是一种泛型技术。虚函数表每个含有虚函数的类都… C 中的虚函数的作用主要是实现了多态的机制。关于多态简而言之就是用父类型别的指针指向其子类的实例然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”这是一种泛型技术。虚函数表每个含有虚函数的类都有一个虚函数表Virtual Table来实现的。简称为V-Table。C 的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下。这意味着我们通过对象实例的地址得到这张虚函数表然后就可以遍历其中函数指针并调用相应的函数。1、 每一个类都有虚函数列表。2、 虚表可以继承如果子类没有重写虚函数那么子类虚表中仍然会有该函数的地址只不过这个地址指向的是基类的虚函数实现。如果基类3个虚函数那么基类的虚表中就有三项虚函数地址派生类也会有虚表至少有三项如果重写了相应的虚函数那么虚表中的地址就会改变指向自身的虚函数实现。如果派生类有自己的虚函数那么虚表中就会添加该项。3、 派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同子类独有的虚函数放在后面。当定义一个有虚函数类的对象时对象的第一块的内存空间就是一个指向虚函数列表的指针。在这举个例子假设我们有这样的一个类由于例程的操作环境是64位系统所以用long*强转。其中(long*)(b)就是虚函数表地址(long*)*(long*)(b)就是第一个函数地址代码运行结果如下在程序中取出对象b的地址根据对象的布局可以得出就是虚表的地址根据这个地址可以把虚表的第一个内存单元的内容取出然后强制转换成一个函数指针利用这个函数指针来访问虚函数。又因为虚表是连续的利用每次 1可以来访问下一个内存单元。如果对代码不理解的话可以看这幅图就会懂了注意虚函数表在最后会有一个结束标志为1说明还有虚表为0表示没有虚表了 。编译器不同结束标志可能存在差异下面将分别具体说明“无虚函数覆盖”和“有虚函数覆盖”时的虚函数表的情况。一无虚函数覆盖没有任何的继承虚函数表如下图根据示意图编写的代码如下图所示和上一个程序一样根据取出虚表里面的地址强制转换成函数指针同样利用虚表的连续性每次指针 1调用对应的虚函数。可以得出虚函数按照其声明顺序存放于虚函数表中的子类自己的虚函数是排在父类虚函数之后的。运行结果如下图二一般继承有虚函数覆盖如果子类中有虚函数重载了父类的虚函数会是一个什么样子假设我们有下面这样的一个继承关系。如图所示在这个类的设计中只覆盖了父类的一个函数f()。那么对于派生类的实例其虚函数表会是下面的一个样子从表中可以看到下面几点1覆盖的f()函数被放到了虚表中原来父类虚函数的位置。2没有被覆盖的函数依旧。这样就会出现虚调用base *b new Derive();b-f();由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代于是在实际调用发生时是Derive::f()被调用了。这就实现了多态。下面我们用一个示例代码来看一下运行结果如下,确实如我们以上分析的那样由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代三多重继承无虚函数覆盖下面我们再看看多重继承的情况对于子类实例中的虚函数表是下面这个样子从图上我们可以看到1每个父类都有自己的虚表。2 子类的成员函数被放到了第一个父类的表中。所谓的第一个父类是按照声明顺序来判断的这样做就是为了解决不同的父类类型的指针指向同一个子类实例而能够调用到实际的函数。下面我们根据上图来实现一下运行结果如下在这个程序中子类有多个父类因此从每个父类都继承了一个虚表因此会有3个虚表根据代码和运行结果会发现排列的顺序和继承的顺序一样子类自己的虚函数排在第一个虚表的后面。程序中没有改写虚函数 因此没有覆盖。同时主函数中应用的是一个二重指针利用二维数组取每个虚函数地址。四多重继承有虚函数覆盖下面我们再来看看如果发生虚函数覆盖的情况。下图中我们在子类中覆盖了父类的f()函数。子类虚函数列表如图所示三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样我们就可以任一静态类型的父类来指向子类并调用子类的f()了。子类虚函数列表访问代码如下程序运行结果如下所示本程序中子类重写了f函数把所有父类里面的f函数都屏蔽了。在虚表中父类f函数的位置全部换成了子类f函数的地址。因此在输出时父类的f函数没有了全部是子类f函数的输出。