RSS

C++中的虚函数(virtual function)

在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。

这种技术可以让父类的指针有多种形态,这是一种泛型技术。

即如果这个指针/引用是基类对象的指针/引用就调用基类的方法;如果指针/引用是派生类对象的指针/引用就调用派生类的方法,当然如果派生类中没有此方法,就会向上到基类里面去寻找相应的方法。

由于编写代码的时候并不能确定被调用的是基类的函数还是哪个派生类的函数,所以被成为”虚”函数。

参考资料1深度探索C++对象模型,StanleyB.Lippman,侯捷译2DesignPatterns,ElementsofReusableObject-OrientedSoftware,GOF,\\————–siwuxie095**多态**多态是面向对象的三大特征之一,其它两大特征分别是封装和继承!(https://img-blog.csdn.net/20170504111644170)!(https://img-blog.csdn.net/20170504111644545)所谓多态,简单来说,就是当发出一条命令时,不同的对象接收到同样的命令后所做出的动作是不同的!(https://img-blog.csdn.net/20170504111644880)而书本上的定义则是:!(https://img-blog.csdn.net/20170504111645185)其实就是在说两个概念:静态多态和动态多态!(https://img-blog.csdn.net/20170504111645473)**静态多态**静态多态,也叫早绑定看如下实例:定义一个矩形类:Rect,其中有两个同名成员函数:calcArea(),显然二者互为重载(名字相同,参数可辨)!(https://img-blog.csdn.net/20170504111646404)在使用时:当实例化一个Rect的对象后,就可以通过对象分别调用这两个函数,计算机在编译时,就会自动调用对应的函数!(https://img-blog.csdn.net/20170504111647551)即程序运行之前,在编译阶段就已经确定下来到底要使用哪个函数,可见:很早就已经将函数编译进去了,称这种情况为早绑定或静态多态**动态多态**动态多态,也叫晚绑定看如下实例:当前要计算面积,于是分别给圆形和矩形下达计算面积的指令,作为圆形来说,它有自己计算面积的方法,作为矩形来说,它也有自己计算面积的方法!(https://img-blog.csdn.net/20170504111648451)显然,两种计算面积的方法肯定不同,即对不同对象下达相同指令,却做着不同的操作,称之为晚绑定或动态多态动态多态是有前提的,它必须以封装和继承为基础。

但可以通过让友元函数调用虚拟成员函数来解决友元的虚拟问题。

普通函数只能被重载,不能被重写。

由于A::fun()和A::fun2()是虚函数,所以&A::fun和&A::fun2获得的不是函数的地址,而是一段间接获得虚函数地址的一段代码的地址,我们形象地把这段代码看作那段CallVirtualFun。

所以,用户不能创建类的实例,只能创建它的派生类的实例。

由于A::fun()和A::fun2()是虚函数,所以&A::fun和&A::fun2获得的不是函数的地址,而是一段间接获得虚函数地址的一段代码的地址,我们形象地把这段代码看作那段CallVirtualFun。

对于上述程序,由于pb实际指向的对象类型是Derive,因此vptr指向的Derive类的vtable,当调用pb->func()时,根据虚表中的函数地址找到的就是Derive类的func()函数。

派生类有圆形Circle、矩形mRect两个,两个派生类都需继承计算面积的方法,但是两种派生类的计算方式又不一样,所以在派生类中需要对**calcArea**重新定义为派生类自己的方法,在父类定义**calcArea**方法时使用virtual关键字,可以帮助多态的实现,让指向派生类的指针或引用在调用calcArea方法的时候,可以调用自己的calcArea方法,若不加,将调用父类的**calcArea**方法。

其原因是由C++中多重继承的内存布局说起。

友元不是成员函数,只有成员函数才可以是虚拟的,因此友元不能是虚拟函数。

我们自己编写这样一个例子:include”stdio.h”include”conio.h”classParent用任意版本的VisualC++或BorlandC++编译并运行,输入一个小写字母c,得到下面的结果:Thisisparent,function1Thisischild,function2为什么会有第一行的结果呢?因为我们是用一个Parent类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了第一行的结果。

构造函数本身就是要初始化实例,那使用虚函数也没有实际意义呀。

•抽象类是不能定义对象的。


Your Comment