C++多态性基本概念 包括虚函数和纯虚函数(通透版).doc_第1页
C++多态性基本概念 包括虚函数和纯虚函数(通透版).doc_第2页
C++多态性基本概念 包括虚函数和纯虚函数(通透版).doc_第3页
C++多态性基本概念 包括虚函数和纯虚函数(通透版).doc_第4页
C++多态性基本概念 包括虚函数和纯虚函数(通透版).doc_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

C+多态性基本概念 包括虚函数和纯虚函数C+编程语言是一款应用广泛,支持多种程序设计的计算机编程语言。我们今天就会为大家详细介绍其中C+多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握。前几天笔试的时候碰到考C+多态性的题目,因为不是自己的专业不是纯做软件开发,C+学习不是很好,做得有点混乱。回来以后立刻查了相关资料,大概明白了一点,可能以后解题的时候不会乱了。先摘下一些网上的书上的基本概念。多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。C+多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这里我觉得要补充,重写的话可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C+多态性)而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。但这并没有体现多态性。多态与非多态的实质区别就是函数地址是早绑定还是晚绑定。如果函数的调用,在编译器编译期间就可以确定函数的调用地址,并生产代码,是静态的,就是说地址是早绑定的。而如果函数调用的地址不能在编译器期间确定,需要在运行时才确定,这就属于晚绑定。那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,即没有利用C+多态性,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。笔试的题目#include class A public: void foo() printf(1); virtual void fuu() printf(2); ; class B:public A public: void foo() printf(3); void fuu() printf(4); ; int main() A a; B b; A *p = &a; p-foo(); p-fuu(); p = &b; p-foo();p-fuu(); return 0;第一个p-foo()和p-fuu()都很好理解,本身是基类指针,指向的又是基类对象,调用的都是基类本身的函数,因此输出结果就是1、2。第二个输出结果就是1、4。p-foo()和p-fuu()则是基类指针指向子类对象,正式体现多态的用法,p-foo()由于指针是个基类指针,指向是一个固定偏移量的函数,因此此时指向的就只能是基类的foo()函数的代码了,因此输出的结果还是1。而p-fuu()指针是基类指针,指向的fuu是一个虚函数,由于每个虚函数都有一个虚函数列表,此时p调用fuu()并不是直接调用函数,而是通过虚函数列表找到相应的函数的地址,因此根据指向的对象不同,函数地址也将不同,这里将找到对应的子类的fuu()函数的地址,因此输出的结果也会是子类的结果4.笔试的题目中还有一个另类测试方法。即B *ptr = (B *)&a; ptr-foo(); ptr-fuu();问这两调用的输出结果。这是一个用子类的指针去指向一个强制转换为子类地址的基类对象。结果,这两句调用的输出结果是3,2。并不是很理解这种用法,从原理上来解释,由于B是子类指针,虽然被赋予了基类对象地址,但是ptr-foo()在调用的时候,由于地址偏移量固定,偏移量是子类对象的偏移量,于是即使在指向了一个基类对象的情况下,还是调用到了子类的函数,虽然可能从始到终都没有子类对象的实例化出现。而ptr-fuu()的调用,可能还是因为C+多态性的原因,由于指向的是一个基类对象,通过虚函数列表的引用,找到了基类中foo()函数的地址,因此调用了基类的函数。由此可见多态性的强大,可以适应各种变化,不论指针是基类的还是子类的,都能找到正确的实现方法。/小结:1.有virtual才可能发生多态现象2.不发生多态(无virtual)调用就按原类型调用#include using namespace std;class Basepublic:virtual void f(float x) cout Base:f(float) x endl; void g(float x) cout Base:g(float) x endl; void h(float x) cout Base:h(float) x endl; ;class Derived : public Basepublic:virtual void f(float x) cout Derived:f(float) x endl; /多态void g(int x) cout Derived:g(int) x endl; /覆盖void h(float x) cout Derived:h(float) x f(3.14f); / Derived:f(float) 3.14pd-f(3.14f); / Derived:f(float) 3.14/ Bad : behavior depends on type of the pointerpb-g(3.14f); / Base:g(float) 3.14pd-g(3.14f); / Derived:g(int) 3 (surprise!)/ Bad : behavior depends on type of the pointerpb-h(3.14f); / Base:h(float) 3.14 (surprise!)pd-h(3.14f); / Derived:h(float) 3.14system(pause);/-C+纯虚函数一、定义纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0” virtual void funtion1()=0 二、引入原因1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。 2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。 三、相似概念1、多态性 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动作。C+支持两种多态性:编译时多态性,运行时多态性。 a.编译时多态性:通过重载函数实现 b 运行时多态性:通过虚函数实现。 2、虚函数 虚函数是在基类中被声明为virtual,并在派生类中重新定义的成员函数,可实现成员函数的动态重载 3、抽象类 包含纯虚函数的类称为抽象类。由于抽象类包含了没有定义的纯虚函数,所以不能定义抽象类的对象。 编辑本段程序举例基类: class A public: A(); void f1(); virtual void f2(); virtual void f3()=0; virtual A(); ; 子类: class B : public A public: B(); void f1(); void f2(); void f3(); virtual B(); ; 主函数: int main(int argc, char* argv) A *m_j=new B(); m_j-f1(); m_j-f2(); m_j-f3(); delete m_j; return 0; f1()是一个普通的重载. 调用m_j-f1();会去调用A类中的f1(),它是在我们写好代码的时候就会定好的. 也就是根据它是由A类定义的,这样就调用这个类的函数. f2()是虚函数. 调用m_j-f2();会调用m_j中到底保存的对象中,对应的这个函数.这是由于new的B对象. f3()与f2()一样,只是在基类中不需要写函数实现.虚函数和纯虚函数在面向对象的C+语言中,虚函数(virtual function)是一个非常重要的概念。因为它充分体现了面向对象思想中的继承和多态性这两大特性,在C+语言里应用极广。比如在微软的MFC类库中,你会发现很多函数都有virtual关键字,也就是说,它们都是虚函数。难怪有人甚至称虚函数是C+语言的精髓。那么,什么是虚函数呢,我们先来看看微软的解释:虚函数是指一个类中你希望重载的成员函数,当你用一个基类指针或引用指向一个继承类对象的时候,你调用一个虚函数,实际调用的是继承类的版本。摘自MSDN这个定义说得不是很明白。MSDN中还给出了一个例子,但是它的例子也并不能很好的说明问题。我们自己编写这样一个例子:include stdio.hinclude conio.hclass Parentpublic: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 Parentvoid Function1();void Function2(); child;void Child:Function1()printf(This is child,function1n);void Child:Function2()printf(This is child,function2n);int main(int argc, char* argv)Parent *p; / 定义一个基类指针if(_getch()=c) / 如果输入一个小写字母cp=&child; / 指向继承类对象elsep=&parent; / 否则指向基类对象p-Function1(); / 这里在编译时会直接给出Parent:Function1()的入口地址。p-Function2(); / 注意这里,执行的是哪一个Function2?return 0;用任意版本的Visual C+或Borland C+编译并运行,输入一个小写字母c,得到下面的结果:This is parent,function1This is child,function2为什么会有第一行的结果呢?因为我们是用一个Parent类的指针调用函数Fuction1(),虽然实际上这个指针指向的是Child类的对象,但编译器无法知道这一事实(直到运行的时候,程序才可以根据用户的输入判断出指针指向的对象),它只能按照调用Parent类的函数来理解并编译,所以我们看到了第一行的结果。那么第二行的结果又是怎么回事呢?我们注意到,Function2()函数在基类中被virtual关键字修饰,也就是说,它是一个虚函数。虚函数最关键的特点是“动态联编”,它可以在运行时判断指针指向的对象,并自动调用相应的函数。如果我们在运行上面的程序时任意输入一个非c的字符,结果如下:This is parent,function1This is parent,function2请注意看第二行,它的结果出现了变化。程序中仅仅调用了一个Function2()函数,却可以根据用户的输入自动决定到底调用基类中的Function2还是继承类中的Function2,这就是虚函数的作用。我们知道,在MFC中,很多类都是需要你继承的,它们的成员函数很多都要重载,比如编写MFC应用程序最常用的CView:OnDraw(CDC*)函数,就必须重载使用。把它定义为虚函数(实际上,在MFC中OnDraw不仅是虚函数,还是纯虚函数),可以保证时刻调用的是用户自己编写的OnDraw。虚函数的重要用途在这里可见一斑。再看下面的-摘自:C+中虚函数和纯虚函数的概念,差别和分别存在的原因首先:强调一个概念定义一个函数为虚函数,不代表函数为不被实现的函数定义他为虚函数是为了允许用基类的指针来调用子类的这个函数定义一个函数为纯虚函数,才代表函数没有被实现定义他是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。对继承的影响:普通的类(没有虚函数,纯虚函数)就可以被继承,而且工作的相当好关于这个问题有以下疑问:纯虚函数难道就是为了实现接口?接口存在的意义?我实在弄不懂,我干嘛要预先定义好?未来的事情本难料就等有一天我的类中需要使用某个函数,在添加一个函数不久可以?关于实例化一个类:有纯虚函数的类是不可能生成类对象的,如果没有纯虚函数则可以。比如:class CApublic:virtual void fun() = 0; / 说明fun函数为纯虚函数virtual void fun1();class CBpublic:virtual void fun();virtual void fun1();/ CA,CB类的实现.void main()CA a; / 不允许,因为类CA中有纯虚函数CB b; / 可以,因为类CB中没有纯虚函数.-虚函数在多态中间的使用:多态一般就是通过指向基类的指针来实现的。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 animalpublic:animal();animal();void fun1(animal *maybedog_maybehorse);virtual void born();void animal:fun1(animal *maybedog_maybehorse)maybedog_maybehorse-born();animal:animal()animal:animal()void animal:born()cout animal;class dog: public animalpublic:dog();dog();virtual void born();

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论