4.1 C++多态的概念及前提条件
参考: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中的主函数修改成以下形式再来分析一下函数运行结果。
在这个主函数中同样是声明一个基类类型的指针,之后通过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++多态的概念及前提条件的更多相关文章
- C++学习22 多态的概念及前提条件
在<C++基类和派生类的赋值>一节中讲到,基类的指针也可以指向派生类对象.请看下面的例子: #include <iostream> using namespace std; c ...
- 程序基石系列之C++多态的前提条件
准备知识 C++中多态(polymorphism)有下面三个前提条件: 必须存在一个继承体系结构. 继承体系结构中的一些类必须具有同名的virtual成员函数(virtualkeyword) 至少有一 ...
- JAVA之旅(八)——多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例
JAVA之旅(八)--多态的体现,前提,好处,应用,转型,instanceof,多态中成员变量的特点,多态的案例 学习是不能停止的 一.多态 我们今天又要学习一个新的概念了,就是多态,它是面向对象的第 ...
- C++中多态的概念和意义
1,函数重写回顾: 1,父类中被重写的函数依然会继承给子类: 2,子类中重写的函数将覆盖父类中的函数: 1,重写父类当中提供的函数是因为父类当中提供的这个函数版本不能满足我们的需求,因此我们要重写: ...
- Dataguard配置前提条件
Data Guard配置前提条件 配置Data Guard必须保证以下前提条件: 1.Data Guard是Oracle企业版的组件.Oracle标准版里没有这个控件.所以Data Guard配置所使 ...
- java foreach遍历的前提条件
自我总结,欢迎拍砖,不胜感激! 目的: 加深foreach遍历的影响 证明:foreach遍历的前提条件是:list !=null ,而不是:list !=null && list.s ...
- centos7源码安装Python3的前提条件
centos7源码安装Python3的前提条件: # yum -y install openssl-devel bzip2-devel expat-devel gdbm-devel readline- ...
- 安装爬虫 scrapy 框架前提条件
安装爬虫 scrapy 框架前提条件 (不然 会 报错) pip install pypiwin32
- php接口和多态的概念以及简单应用
接口是面向对象中的一个重要特性,也是面向对象开发不可缺少的一个概念,下面简单说一下接口的概念,先看一段简单的代码: interface ICanEat { public function eat($f ...
随机推荐
- java maven项目 pom.xml plugin 报错, build path 找不到 jconsole-1.8.0.jar 和 tools-1.8.0.jar 包
maven项目pom.xml突然报错,在Java Build Path 中并没有引用的jar包出现在了Maven Dependencies的依赖包中. 这个错误直接导致了pom.xml文件中 < ...
- springmvc如何进行热部署开发
1.场景还原 在工程量大的情况下,tomcat运行部署一次会花费相当多的时间,这样太 浪费人力以及时间了:今天笔者将 讲解一下如何配置springmvc工程的热部署 2.实现方案 其实很简单! ①在t ...
- Google云平台使用方法 | Hail | GWAS | 分布式回归 | LASSO
参考: Hail Hail - Tutorial windows也可以安装:Spark在Windows下的环境搭建 spark-2.2.0-bin-hadoop2.7 - Hail依赖的平台,并行处 ...
- jquery中的全选、反选、全不选和单删、批删
HTML页面 <!doctype html><html lang="en"><head> <meta charset="UTF- ...
- pytorch初步学习(一):数据读取
最近从tensorflow转向pytorch,感受到了动态调试的方便,也感受到了一些地方的不同. 所有实验都是基于uint16类型的单通道灰度图片. 一开始尝试用opencv中的cv.imread读取 ...
- 『C++』STL容器入门
最近在学习opencv,因为C++基础很烂,所以遇到了不少问题,其中STL模块也是没少接触,特此简单了解一下STL的容器类型(主要是Vector)和迭代器的简单用法. C++ STL(标准模板库)是一 ...
- Object.keys的使用
链接:https://www.nowcoder.com/questionTerminal/52c41b84e32a4158883cb112a1d1f850来源:牛客网 输出对象中值大于2的key的数组 ...
- python-部分redis
数据量非常大时想向数据库中保存的时候,可以在中间加一个队列(队列的长度可以控制),可能数据库一个个取会效率慢一些,但是不会服务端不会蹦 redis: 端口6379 1.本质:向内存中存数据 2.对内存 ...
- js时间与时间戳互相转换
var _time1 = Date.parse(new Date(‘2017-05-02 00:00:00’))/1000; //将设定的日期转换为时间戳 _time1 = getLocalTime( ...
- spring cloud服务发现注解之@EnableDiscoveryClient与@EnableEurekaClient
使用服务发现的时候提到了两种注解,一种为@EnableDiscoveryClient,一种为@EnableEurekaClient,用法上基本一致,今天就来讲下两者,下文是从stackoverflow ...