C++的虚函数和多态

  1.虚函数

     将基类中的某个函数声明为虚拟函数(在函数原型前加virtual),然后在每个派生类中改写该虚拟函数,声明虚拟函数时,在基类的函数原型前加关键字virtual;

     函数一旦声明为虚拟函数,即使派生类在改写(重新定义)它时没有将其声明为虚拟函数,它从该点之后的继承层次结构中仍然是虚拟函数,但是良好的编程习惯是在每次定义时都声明为虚拟函数;

     派生类没有定义虚拟函数时,可以简单继承其直接基类(注意这里是直接基类)的虚拟函数定义。

     如果基类中的draw(举例)函数已经声明为virtual,用基类类型的指针或者引用来指向派生类对象,并用该指针调用drqw函数,程序会根据对象的类型(而不是指针的类型)动态在运行时选择相应的派生类的draw函数

     但如果用名称和圆点成员选择操作符引用特定对象以调用虚拟函数,被调用的虚拟函数是在编译时确定的(即静态绑定,编译时将调用的方法和具体对象绑定为静态绑定,运行时将调用方法和具体对象绑定为动态绑定),调用的虚拟函数就是该特定对象的类中定义的虚拟函数。

     注意两点前提:

      (1)基类类型的指针(声明时为基类类型)或者基类的引用;

      (2)该指针指向具体的派生类。

 

     从而导致:

             调用派生类对象的类定义的方法。

  2.抽象类和具体类

 

    抽象基类(一定要注意基类和抽象基类的区别,两者不是同一个概念)不能实例化为对象,抽象类唯一的用途是为其他类提供合适的基类,以便他们可以继承或者实现接口

    基类和派生类意义对应

    抽象类和具体类意义对应,抽象类不能实例化,只有具体类才能够被实例化。

    抽象基类代表的含义太广泛很难定义具体的对象,具体类要做的工作,提供更加明确的含义使之实例化为具体的对象。

 

   将一个类声明为抽象类的方法,将它的虚拟函数声明为pure,即纯虚拟函数(虚拟函数有函数定义,因此普通基类可以被实例化,而纯虚拟函数没有函数定义,只有函数声明,因此不能够被实例化)

       声明方法,声明时初始化为0,即:Virtual double earnings() const=0;

    

       如果类从带有纯虚拟函数的类派生而来,且该派生类中没有提供该纯虚拟函数的定义(纯虚拟函数必然没有函数定义),那么这个纯虚拟函数在该派生类中仍然是纯虚的,这个派生类仍然是抽象类。

      

 

  3.多态性

     C++支持多态,即通过继承相关的不同的类,对象能够对同一个函数调用作出不同响应。

     多态性是通过虚拟函数(注意是虚拟函数,而不是纯虚拟函数)实现的,通过基类(注意这里并没有说基类一定是抽象的)指针或引用(注意什么叫做基类指针或引用,声明时的类型定义)指向某一具体的对象,通过指针调用虚拟函数时,C++会在与对象关联的派生类中选择正确的改写过的函数。 

 

     多态的两个前提条件

     (1)基类类型的指针或者引用

     (2)通过指针调用的函数必须是虚拟函数

     这样,该指针指向某个派生类对象时,C++在运行中就会调用该派生类对象的类中定义的方法。

       如果调用的函数不是虚拟函数,则通过基类类型指针调用的是基类中的方法;

       如果用派生类类型的指针调用成员函数,则调用的是派生类版本的成员函数。

 

使用虚拟函数和多态性,可使成员函数调用因为指针所指的对象不同而做出不同的反应。

 

以下需要好好理解:

   利用虚拟函数和多态性,程序员可以处理普遍问题而让环境处理特殊问题,即使不知道对象类型的情况下,程序员也可以令各种对象表现出适合这些对象的行为。也就是说,只要有一个基类类型的指针,可以不知道该指针具体指向哪一个派生类对象,只要用该指针调用某一个基类中定义的虚拟函数,执行环境会进行动态绑定(即和相应的对象类中的方法绑定起来),从而自动调用对象类中的改写的方法。

 

   利用虚拟函数和多态性,大大提高了系统的可扩展性(这也正是C++作为系统生成工具语言的原因之一),编写处理多态性行为的软件可以独立于接受这些命令的对象类型之外(即派生类定义和测试软件《其中通过基类指针调用虚拟函数》是分开的),吧能够相应县有关命令的新类型对象添加到原有系统,不必修改原有系统就可以实现,即只要定义好新的类型,并让该类型继承基类,改写基类中的虚拟函数,就可以在测试程序中通过基类类型的指针(指向新类型的对象)调用虚拟函数,从而调用新类型中改写的方法。或者是在客户代码中声明一个类的对象,通过基类类型的指针(指向新对象)调用虚拟函数,从而调用新对象类型中改写的方法。

 

   动态绑定(运行时确定执行哪个对象的方法)

 

  一下两段是关于抽象类的(时刻注意抽象类和普通基类的区别)

  抽象类相当于Java中的Interface概念,即通过定义纯虚函数进行函数声明,而没有具体的函数定义,抽象类可以为类层次结构中的各成员定义接口(即相当于Java中的Interface),其中包含了要在派生类中定义的纯虚拟函数,该层次结构中所有函数都可以通过多态性使用同样的接口。

 

  尽管不能实例化抽象类,但是可以声明抽象类型的指针和引用,实例化具体类的对象时,让这些指针指向具体类对象,这样通过使用指针调用纯虚拟函数,就可以实现派生类对象的多态性操作。

 

  多态性特别适合实现分层的软件系统。

 

  利用多态性编程,可以遍历一个容器,如一个指向类层次结构不同层的对象的指针数组,该数组类型定义为基类类型的指针类型,数组中的每个元素类型都是一个基类类型的指针,每一个指针可以指向具体的派生类类型对象,如数组类型是TwodimensionShape*,而每个元素的值为&Square,&Circle,&Triangle等等,使用多态性时,直接用指针调用虚拟函数即可动态绑定到具体对象的类中改写的方法。

 

  4.新类和动态绑定

  事先无法确定需要使用的类时(当然所有的类都已经定义好了,只是在测试程序中不知道具体需要调用哪一个),多态性和虚拟函数会运行良好,如有新类加入系统时,照样能够运行良好,动态绑定允许增加新类(滞后联编),对于准备编译的虚拟函数调用,不必在编译时知道对象类型,在运行时,虚拟函数调用会和调用的对象的相应成员函数匹配(不编译就可以动态绑定到新的类)。

  动态绑定可以使独立软件开发商在不公开源代码的情况下发行软件,发行的软件可以只包括头文件和对象文件,不必包含源代码,软件卡发着可以利用继承机制从独立软件开发上提供的类中派生出新类,派生类和独立软件开发商提供的类一起运行,也可以通过动态绑定在这些派生类中改写虚拟函数。

 

  5.虚拟析构函数

   在类层次结构中,用多态性动态分配对象时会出现一个问题,如果一个对象(带有非虚析构函数)被delete操作符作用于指向对象的基类类型指针从而显示删除该对象,会调用基类的析构函数(因为不符合多态的一个前提,即析构函数没有定义成为虚拟函数)

  解决方法是声明一个虚拟的基类析构函数,这样可以使所有的派生类析构函数变成虚拟函数,即使派生类的析构函数的基类的析构函数名称不同(名称肯定不同,因为和类同名),这样,如果用delete操作符作用于指向派生类对象的基类类型指针,就会调用合适的类的西沟函数,当派生类对象被删除时,派生类的基类部分也会被删除,在派生类析构函数执行结束之后执行基类析构函数。

 

  构造函数不能是虚拟的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值