参考:http://www.weixueyuan.net/view/6370.html

总结:

  而多态的功能则是将函数名动态绑定到函数入口地址,这样的动态绑定过程称为运行期绑定。

  而在运行期绑定的函数我们称其是多态的。

  通过基类类型的指针根据所指向对象的类型来自动决定调用基类还是派生类的display函数

  要想形成多态必须具备以下三个条件:

  • 必须存在继承关系;
  • 继承关系中必须有同名的虚函数;
  • 存在基类类型的指针或引用,通过该指针或引用调用虚函数。

  

在C++程序中,程序的每一个函数在内存中会被分配一段存储空间,而被分配的存储空间的起始地址则为函数的入口地址。例如我们在设计一个程序时都必须为程序设计一个主函数,主函数同样会在内存中被分配一段存储空间,这段存储空间的起始地址即为函数的入口地址。

在前面的所有列举的程序中,函数的入口地址与函数名是在编译时进行绑定的,我们称之为编译期绑定,而多态的功能则是将函数名动态绑定到函数入口地址,这样的动态绑定过程称为运行期绑定,换句话来说就是函数名与函数入口地址在程序编译时无法绑定到一起,只有等运行的时候才确定函数名与哪一个函数入口绑定到一起。

那么多态到底有什么用处呢?我们不妨来看个例子。在windows操作系统中,我们经常会进行一些关闭操作,比如关闭文件夹、关闭文本文件、关闭播放器窗口等,这些关闭动作对应的函数close假设都继承自同一个基类,但是每一个类都需要有自己的一些特殊功能,比如清理背景、清除缓存等工作,如此一来当我们执行close函数时,我们当然希望根据当前所操作的窗口类型来决定该执行哪一个close函数,因此运行期绑定就可以派上用场了。

编译期绑定是指在程序编译时就将函数名与函数入口地址绑定到一起,运行期绑定是指在程序运行时才将函数名与函数入口地址绑定到一起,而在运行期绑定的函数我们称其是多态的。

为了说明虚函数的必要性,我们先来看一个示例程序。

例1:

#include<iostream>
using namespace std; class base
{
public:
void display(){cout<<"I'm base class!"<<endl;}
}; class derived: public base
{
public:
void display(){cout<<"I'm derived class!"<<endl;} }; int main()
{
base * p;
derived test;
p = &test;
p->display();
return ;
}
这个例子非常简单,两个类,一个是base类,一个是derived类,二者构成继承关系,同时在这两个类中均含有一个display函数,因为函数同名,故在派生类对象中会出现遮蔽现象,即派生类中的display函数会遮蔽基类中的display函数。

在主函数中,定义了一个基类类型的指针p和派生类对象test,之后p指针指向派生类对象test,然后通过指针调用display函数。此程序最终运行结果如下:
    I’m base class!

从结果来看这个程序最终调用的display函数是基类的display函数,而非派生类中的display函数。但此程序的本意是先通过基类类型的指针根据所指向对象的类型来自动决定调用基类还是派生类的display函数。为了实现这样的一种功能,C++提供了多态这一机制。

要想形成多态必须具备以下三个条件:

  • 必须存在继承关系;
  • 继承关系中必须有同名的虚函数;
  • 存在基类类型的指针或引用,通过该指针或引用调用虚函数。

根据这三个条件,我们将例1进行修改,使display函数具有多态特性。修改后程序如例2 所示。

例2:

#include<iostream>
using namespace std; class base
{
public:
virtual void display(){cout<<"I'm base class!"<<endl;}
}; class derived: public base
{
public:
virtual void display(){cout<<"I'm derived class!"<<endl;} }; int main()
{
base * p;
derived test;
p = &test;
p->display();
return ;
}
例2所示程序相对于例1只是在display函数前各添加了一个virtual关键字。我们对照三个多态的构成条件来分析一下,多态首先需要继承关系,derived类继承自base类,因此base类和derived类构成继承关系;其次多态需要同名的虚函数,base类和derived类中都有display函数,同名满足,同时通过添加关键字virtual后,display函数成为虚函数;最后多态需要通过基类类型的指针或引用来调用虚函数,在例2中的主函数中,p即为基类类型指针,并且将该指针指向派生类对象,然后调用display函数。这段程序最终运行结果如下:

I’m derived class!

例2这个程序展示出来的就是多态,display函数通过virtual关键字声明为虚函数,具有多态特性。我们将例2中的主函数修改成以下形式再来分析一下函数运行结果。

int main()
{
base * p = new base;
p->display();
delete p;
p = new derived;
p->display();
delete p;
return ;
}

在这个主函数中同样是声明一个基类类型的指针,之后通过new给指针分配一个基类对象,通过p指针调用display函数,此时不用说肯定是输出“I’m base class!”,因为这中间一直没有涉及到派生类的事情。之后销毁之前new分配的base对象,然后通过new分配一个derived类对象,p指向该派生类对象,通过p指针调用display函数,此时的情况和例2是完全相同的,因此输出“I’m derived class!”,之后再delete销毁派生类对象。修改主函数之后程序输出结果如下:
    I’m base class!
    I’m derived class!

这样的输出结果与我们的分析结果是一致的。

4.1 C++多态的概念及前提条件的更多相关文章

  1. C++学习22 多态的概念及前提条件

    在<C++基类和派生类的赋值>一节中讲到,基类的指针也可以指向派生类对象.请看下面的例子: #include <iostream> using namespace std; c ...

  2. 程序基石系列之C++多态的前提条件

    准备知识 C++中多态(polymorphism)有下面三个前提条件: 必须存在一个继承体系结构. 继承体系结构中的一些类必须具有同名的virtual成员函数(virtualkeyword) 至少有一 ...

  3. JAVA之旅(八)——多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例

    JAVA之旅(八)--多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例 学习是不能停止的 一.多态 我们今天又要学习一个新的概念了,就是多态,它是面向对象的第 ...

  4. C++中多态的概念和意义

    1,函数重写回顾: 1,父类中被重写的函数依然会继承给子类: 2,子类中重写的函数将覆盖父类中的函数: 1,重写父类当中提供的函数是因为父类当中提供的这个函数版本不能满足我们的需求,因此我们要重写: ...

  5. Dataguard配置前提条件

    Data Guard配置前提条件 配置Data Guard必须保证以下前提条件: 1.Data Guard是Oracle企业版的组件.Oracle标准版里没有这个控件.所以Data Guard配置所使 ...

  6. java foreach遍历的前提条件

    自我总结,欢迎拍砖,不胜感激! 目的: 加深foreach遍历的影响 证明:foreach遍历的前提条件是:list !=null ,而不是:list !=null && list.s ...

  7. centos7源码安装Python3的前提条件

    centos7源码安装Python3的前提条件: # yum -y install openssl-devel bzip2-devel expat-devel gdbm-devel readline- ...

  8. 安装爬虫 scrapy 框架前提条件

    安装爬虫 scrapy 框架前提条件 (不然 会 报错) pip install pypiwin32

  9. php接口和多态的概念以及简单应用

    接口是面向对象中的一个重要特性,也是面向对象开发不可缺少的一个概念,下面简单说一下接口的概念,先看一段简单的代码: interface ICanEat { public function eat($f ...

随机推荐

  1. word中的交叉引用

    分别使用“交叉引用”依次插入所需应用文献编号范围的第一个和最后一个. 所需引用处出现“[1][3]”   在引用处对两个编号操作:点击鼠标右键-选择“切换域代码”. [1]变为” {REF _Ref4 ...

  2. 怎么从bam文件中提取出比对OR没比对上的paired reads | bamToFastq | STAR

    折腾这么多都是白瞎,STAR就有输出没有别对上的pair-end reads的功能 参见:How To Filter Mapped Reads With Samtools I had the same ...

  3. 20170906xlVBA_RecursionGetFiles

    Dim Dic As Object Sub GetFileName() Dim FolderPath As String Set Dic = CreateObject("Scripting. ...

  4. 大div中嵌套小div,点击大div时隐藏,点击小div不隐藏

    给小div添加一个click事件 <div onClick="event.cancelBubble = true">  //小div

  5. javascript 两张图片切换 三目运算符

    <body> <script> function changeImage(){ var s = document.getElementById('myimage'); s.sr ...

  6. php数组的逐行写入文件与读取

    <?php /** * * 对数组$arr1=['Apple Orange Banana Strawberry'] 写入文件,并读取 **/ class IoFile { private $pa ...

  7. php实现队列

    <?php //双向队列的实现 class DoubleEndedQueue{ public $elements; public function __construct(){//析构函数,创建 ...

  8. 统计数组中各个元素出现的次数,元素取值范围为:1到N

     问题描述: * 给定一个整数数组a,长度为N,元素取值范围为[1,N]. * 统计各个元素出现的次数,要求时间复杂度为O(N),空间复杂度为O(1). * 可以改变原来数组结构.  思路: * 从第 ...

  9. linux Boot目录满了之后的解决方法

    boot目录为什么会满? Linux默认分区时,boot分区就200多M,按理说也不小,足够了(实际也就几十M),但是内核经常性的升级,而且自己又不自动卸载,于是该目录下旧的内核文件越积越多,最后就满 ...

  10. php调用oracle存储

    //todo 调用oracle 存储$config = //数据库配置文件 里面包含 用户密码和host和端口以及dbname $conn = oci_connect($config['usernam ...