2022年虚函数和纯虚函数 .pdf
虚函数和纯虚函数在面向对象的C+语言中,虚函数(virtual function)是一个非常重要的概念。因为它充分体现了面向对象思想中的继承和多态性这两大特性,在C+语言里应用极广。比如在微软的 MFC类库中, 你会发现很多函数都有virtual关键字, 也就是说,它们都是虚函数。难怪有人甚至称虚函数是C+语言的精髓。那么,什么是虚函数呢,我们先来看看微软的解释:虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。摘自MSDN 这个定义说得不是很明白。MSDN 中还给出了一个例子,但是它的例子也并不能很好的说明问题。我们自己编写这样一个例子:include stdio.h include conio.h class Parent public: char data20; void Function1(); virtual void Function2(); / 这里声明Function2是虚函数parent; void Parent:Function1() printf(This is parent,function1n); void Parent:Function2() printf(This is parent,function2n); class Child:public Parent void Function1(); void Function2(); child; void Child:Function1() printf(This is child,function1n); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 8 页 - - - - - - - - - void Child:Function2() printf(This is child,function2n); int main(int argc, char* argv) Parent *p; / 定义一个基类指针if(_getch()=c) / 如果输入一个小写字母c p=&child; / 指向继承类对象else p=&parent; / 否则指向基类对象p-Function1(); / 这里在编译时会直接给出Parent:Function1()的入口地址。p-Function2(); / 注意这里,执行的是哪一个Function2 ?return 0; 用任意版本的Visual C+ 或 Borland C+ 编译并运行,输入一个小写字母c,得到下面的结果:This is parent,function1 This is child,function2 为什么会有第一行的结果呢?因为我们是用一个Parent 类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent 类的函数来理解并编译,所以我们看到了第一行的结果。那么第二行的结果又是怎么回事呢?我们注意到,Function2()函数在基类中被virtual关键字修饰,也就是说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。如果我们在运行上面的程序时任意输入一个非 c 的字符,结果如下:This is parent,function1 This is parent,function2 请注意看第二行,它的结果出现了变化。程序中仅仅调用了一个Function2()函数,却可以根据用户的输入自动决定到底调用基类中的Function2还是继承类中的Function2 ,这就是虚函数的作用。 我们知道,在MFC 中,很多类都是需要你继承的,它们的成员函数很多都要重载,比如编写MFC 应用程序最常用的CView:OnDraw(CDC*) 函数,就必须重载使用。把它定义为虚函数(实际上,在MFC 中 OnDraw不仅是虚函数,还是纯虚函数),可以保证时刻调用的是用户自己编写的OnDraw 。虚函数的重要用途在这里可见一斑。再看下面的- 摘自: C+ 中虚函数和纯虚函数的概念,差别和分别存在的原因名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 8 页 - - - - - - - - - 首先:强调一个概念定义一个函数为虚函数,不代表函数为不被实现的函数定义他为虚函数是为了允许用基类的指针来调用子类的这个函数定义一个函数为纯虚函数,才代表函数没有被实现定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。对继承的影响:普通的类(没有虚函数,纯虚函数)就可以被继承,而且工作的相当好关于这个问题有以下疑问:纯虚函数难道就是为了实现接口?接口存在的意义?我实在弄不懂,我干嘛要预先定义好?未来的事情本难料就等有一天我的类中需要使用某个函数,在添加一个函数不久可以?关于实例化一个类:有纯虚函数的类是不可能生成类对象的,如果没有纯虚函数则可以。比如:class CA public: virtual void fun() = 0; / 说明 fun 函数为纯虚函数virtual void fun1(); ; class CB public: virtual void fun(); virtual void fun1(); ; / CA ,CB类的实现. void main() CA a; / 不允许,因为类CA中有纯虚函数CB b; / 可以,因为类CB中没有纯虚函数. - 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 8 页 - - - - - - - - - 虚函数在多态中间的使用:多态一般就是通过指向基类的指针来实现的。dog mydogwangwang; mydogwangwang.born(); 一定是返回“ dog”那么horse myhorsepipi; myhorsepipi.born(); 一定是返回“ horse ”也是多态呀?/ 有一点你必须明白,就是用父类的指针在运行时刻来调用子类: 例如,有个函数是这样的:void animal:fun1(animal *maybedog_maybehorse) maybedog_maybehorse-born(); 参数 maybedog_maybehorse 在编译时刻并不知道传进来的是dog 类还是 horse 类, 所以就把它设定为animal 类,具体到运行时决定了才决定用那个函数。也就是说用父类指针通过虚函数来决定运行时刻到底是谁而指向谁的函数。/ / 用虚函数#include class animal public: animal(); animal(); void fun1(animal *maybedog_maybehorse); virtual void born(); ; void animal:fun1(animal *maybedog_maybehorse) maybedog_maybehorse-born(); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 8 页 - - - - - - - - - animal:animal() animal:animal() void animal:born() cout animal; class dog: public animal public: dog(); dog(); virtual void born(); ; dog:dog() dog:dog() void dog:born() coutdog; class horse:public animal public: horse(); horse(); virtual void born(); ; horse:horse() horse:horse() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 8 页 - - - - - - - - - void horse:born() couthorse; void main() animal a; dog b; horse c; a.fun1(&c); /output: horse / / 不用虚函数#include class animal public: animal(); animal(); void fun1(animal *maybedog_maybehorse); void born(); ; void animal:fun1(animal *maybedog_maybehorse) maybedog_maybehorse-born(); animal:animal() animal:animal() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 8 页 - - - - - - - - - void animal:born() cout animal; class dog: public animal public: dog(); dog(); void born(); ; dog:dog() dog:dog() void dog:born() coutdog; class horse:public animal public: horse(); horse(); void born(); ; horse:horse() horse:horse() void horse:born() couthorse; void main() 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 8 页 - - - - - - - - - animal a; dog b; horse c; a.fun1(&c); output: animal - 有纯虚函数的类是抽象类,不能生成对象,只能派生。他派生的类的纯虚函数没有被改写,那么,它的派生类还是个抽象类。- 定义纯虚函数就是为了让基类不可实例化化, 因为实例化这样的抽象数据结构本身并没有意义. 或者给出实现也没有意义实际上我个人认为纯虚函数的引入, 是出于两个目的, 1. 为了安全 . 因为避免任何需要明确但是因为不小心而导致的未知的结果. 提醒子类去做应做的实现. 2. 为了效率 , 不是程序执行的效率, 而是为了编码的效率名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 8 页 - - - - - - - - -