Python
Java
PHP
IOS
Android
Nodejs
JavaScript
Html5
Windows
Ubuntu
Linux
多态性之编译期多态和运行期多态(C++版)
2023-05-16
多态性之编译期多态和运行期多态(C++版)
C++中最为经典的就是多态性,多态性充分体现了面向对象的思想,并且是C++与C的最大区别之一。多态性分为编译期多态和运行期多态,也称为静态多态和动态多态,有些人也称其为编译时多态和运行时多态,不管什么称呼,万变不离其宗,一个是编译期的静态的多态,一个是运行期的动态的多态,那么它们在C++中分别体现在哪里呢?又有什么区别呢?下面将详细介绍(重点是运行期多态)。
1. 编译期多态(静态多态)
编译期多态,正如其名,就是在编译期确定的一种多态性。这个在C++中主要体现在函数模板,这里需要注意的是函数重载和多态无关,很多地方把函数重载也误认为是编译期多态,这是错误的。
那么函数模板是如何体现编译期多态的呢?下面举一个简单的例子就可以明白。
[cpp] view plain copy
// 例1:函数模板体现出编译期多态
#include <iostream>
template
<
typename
T>
T add(T a, T b)
{
T c = a + b;
return
c;
}
int
main()
{
int
i1 = 1;
int
i2 = 2;
int
iResult = 0;
iResult = add(i1, i2);
std::cout <<
"The result of integer is "
<< iResult << std::endl;
double
d1 = 1.1;
double
d2 = 2.2;
double
dResult = 0;
dResult = add(d1, d2);
std::cout <<
"The result of double is "
<< dResult << std::endl;
return
0;
}
从例1中可以看到,我们定义了一个函数模板add,用来求两个数的和,这两个数的数据类型在使用时才知道。main函数中使用了两个int值的求和以及两个double值的求和,这里就体现了多态性,即在编译期,编译器根据一定的最佳匹配算法确定函数模板的参数类型到底是什么,这就体现了编译期的多种状态。
当说到多态性的时候一般都默认指运行期多态,所以编译期多态大家只要知道是如何表现的就可以了,下面重点来讨论运行期多态。
2. 运行期多态(动态多态)
运行期多态主要是指在程序运行的时候,动态绑定所调用的函数,动态地找到了调用函数的入口地址,从而确定到底调用哪个函数。在C++中,运行期多态主要通过虚函数来实现,并且一定要有继承关系,下面举一个简单的例子来讲解。
[cpp] view plain copy
// 例2:虚函数和继承关系体现运行期多态
#include <iostream>
class
parent
{
public
:
parent() {}
// 父类的虚函数
virtual
void
eat()
{
std::cout <<
"Parent eat."
<< std::endl;
}
// 注意这个并不是虚函数!!!
void
drink()
{
std::cout <<
"Parent drink."
<< std::endl;
}
};
class
child :
public
parent
{
public
:
child () {}
// 子类重写了父类的虚函数
void
eat()
{
std::cout <<
"Child eat."
<< std::endl;
}
// 子类覆盖了父类的函数,注意由于父类的这个函数
// 并不是虚函数,所以不存在继承后重写的说法
void
drink()
{
std::cout <<
"Parent drink."
<< std::endl;
}
// 子类特有的函数
void
childLove()
{
std::cout <<
"Child love playing."
<< std::endl;
}
};
int
main()
{
parent* pa =
new
child();
pa->eat();
// 运行期多态的体现!!!
pa->drink();
// 这里调用的还是父类的drink,所以并不是多态!!!
// pa->childLove(); // 编译出错,父类的指针不能调用父类没有的函数
return
0;
}
[cpp] view plain copy
运行结果:
Child eat.
Parent drink.
例2写得比较完善,说明了很多问题,我们先来看主要的,即多态性的体现。注意,在C++中只能用指针或引用来实现多态,不能通过普通的对象来实现多态,例2中使用的是指针的形式。我们来仔细分析一下。
第一,在我们的父类即parent中定义了一个虚函数(virtual关键字修饰的函数)---eat(),既然是虚函数,那么我们的子类就可以重写这个函数(注意这里强调是重写,而不是重载,也不是覆盖,重要的事情说三遍,是“重写!重写!重写!”)。我们的子类child中重写了eat()函数,至于父类和子类中的其他函数暂且先忽略,后面再讲解,这里只关注多态性相关的点。然后,在main函数中,我们定义了一个父类的指针parent,但是注意,我们虽然定义的是父类的指针,但是我们指向的是子类对象,即new的是child对象,这里涉及到向上转型,即将子类对象向上转型到了父类的指针所指。总之,一句话来说就是定义一个父类指针,指向子类对象,然后我们用父类的这个指针去调用eat()函数,这里就是多态发生的地方。从运行结果可以看到,实际上调用的是子类的eat()函数,并不是父类的eat()函数,这是因为虚函数,父类定义的是虚函数,而子类重写了这个函数,虽然我们定义的是父类指针,但是实际上指向的是子类对象,那么在运行期间,就会找到动态绑定到父类指针上的对象就是子类对象,然后实际上运行期间就是找到了子类对象中eat()函数的入口地址,然后调用了子类的eat()函数,这就是运行期多态。说了这么多,听起来十分绕口,貌似很难理解,但是大家仔细想想,总结成一句话就是:“定义父类指针并指向子类对象,此时用父类指针去调用一个特殊的函数,即父类中该函数是虚函数,而子类重写了这个虚函数,此时调用的这个函数就在运行期间动态地绑定到了指针实际所指的对象,即子类对象,从而去调用子类中的这个函数”。
第二,说完了多态,我们来看看例2中其他需要注意的地方。我们发现在main函数中pa指针还调用了drink()函数,最终的运行结果显然调用的是父类的drink()函数,那么这里为什么没有多态呢?原因很简单,因为drink()函数不是虚函数,所以根本不存在多态这一特性,虽然父类和子类中都有drink()这个函数,但是子类仅仅是覆盖或者说隐藏了父类的drink()函数,并不是重写。而我们的指针是父类指针,所以必定要去调用父类的drink()函数。
第三,我们来看看最后一点。细心的朋友会发现,在子类child中,有一个只有它有而父类没有的函数,即childLove()函数,该函数是子类特有的,和父类没有任何关系,所以在main函数中用pa指针去调用这个函数会出错,因为父类指针根本访问不到子类的这个函数。这也是多态性的一个缺陷,即父类的指针只能访问子类中重写了父类中的那些虚函数,而不能访问子类新增的特有的函数。
最后我们来看一个纯虚函数实现多态性的例子。
[cpp] view plain copy
// 例3:纯虚函数和继承关系体现运行期多态
#include <iostream>
// 父类因为包含纯虚函数,所以该类是抽象类,即不能定义对象
class
parent
{
public
:
parent() {}
// 父类的纯虚函数
virtual
void
eat() = 0;
};
class
child :
public
parent
{
public
:
child () {}
// 子类重写了父类的纯虚函数
void
eat()
{
std::cout <<
"Child eat."
<< std::endl;
}
};
int
main()
{
// parent pa0; // 编译期会出错,因为抽象类不能定义对象
// 注意抽象类虽然不能定义对象,但是可以定义指针用来指向具体类
parent* pa =
new
child();
pa->eat();
// 运行期多态的体现!!!
return
0;
}
[cpp] view plain copy
运行结果:
Child eat.
例3和例2基本上是一样的,省略了很多函数,这里多态性的体现和例2也是完全一样的,之所以举这个例子是为了解释下抽象类用来实现多态的一个理解误区。从例3中就可以看到,纯虚函数就是一个函数后面加上"=0",而没有任何实现,那么包含纯虚函数的类就自然成为了抽象类,而抽象类是不能定义对象的,这也就是为什么main函数中的第一行会出错。注意,继承抽象类的具体类必须实现抽象类中的纯虚函数,否则也会出错。这里想强调一个误区,很多人觉得既然抽象类不能定义对象,那么main函数中的parent *pa为什么没有出错,这是因为抽象类虽然不能定义对象,但是可以定义指针,用来指向具体类,从而实现多态,一定要分清楚C++中的指针、引用、对象三者之间的关系,不要一概而论。
3. 总结
多态性是面向对象中十分重要的一个特性,大家务必要掌握,虽然初学起来感觉很难理解,但是多思考下,看完例子后自己写一个多态性的例子就会清楚一些。平常我们所指的多态性一般都是说运行期多态,所以大家重点掌握运行期多态。本篇文章可能存在一些纰漏,欢迎大家指正,谢谢。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)
多态性之编译期多态和运行期多态(C++版) 的相关文章
【无线通信篇 | Zstack协议栈】CC2530 Zigbee Zstack协议栈组网项目及详细讲解篇
演示视频 xff1a https www bilibili com video BV1Ew411o7Fp 物联网无线通信技术 xff0c ZigBee无线传感网络 CC2530最大的特点就是一个拥有无线收发器 xff08 RF xff09
【春节歌曲回味 | STM32小音乐盒 】PWM+定时器驱动无源蜂鸣器(STM32 HAL库)
l STM32通过PWM与定时器方式控制无源蜂鸣器鸣响 l STM32小音乐盒 xff0c 歌曲进度条图形显示与百分比显示 xff0c 歌曲切换 l 编程使用STM32 HAL库 l IIC OLED界面编程 xff0c 动画实现 PWM
快速上手CH340N电路设计(CH340N USB转串口模块 USB Type-C接口 CH340系列芯片讲解)
一 上模块 二 功能分析 l 芯片 xff1a CH340N l 输入接口 xff1a USB TYPE C l 输出接口 xff1a TTL 5V 3 3V GND TX RX l 指示灯 xff1a 电源 TX RX 三 CH340x芯
制作一个有趣的涂鸦物联网小项目(涂鸦模组SDK开发 CBU BK7231N WiFi+蓝牙模组 HSV彩色控制)
实现的功能 xff1a l APP控制月球灯 l 本地月球灯控制 l APP控制 大白 颜色 xff0c 实现各种颜色变身 l 门状态传感器状态APP显示 l 网络状态指示灯 xff0c 连接服务器长亮 xff0c 断开后闪烁 l 配网按键
有关于串口通信程序的编写
Win 32系统把文件的概念进行了扩展 无论是文件 通信设备 命名管道 邮件槽 磁盘 还是控制台 xff0c 都是用API函数CreateFile来打开或创建的 该函数的声明为 xff1a HANDLE CreateFile LPCTSTR
xilinx zynq-7000 linux下rs422串口通信测试步骤
这几天使用xilinx的zynq 7000系列调试rs422串口 xff0c 期间遇到不少问题 xff0c 好在最终调试完成 xff0c 在这里记录一下调试步骤 我用的soc型号是xc7z100 xff0c 板载了2个rs422外设 xff
C++ 输出Unicode字符的正确方法
正确输出 Unicode 字符 要输出特殊符号首先需要知道它的 Unicode 编码 xff0c 当然 xff0c 直接在剪切板里 copy 对应符号也是可以的 要查询字符对应的 Unicode 编码 xff0c 可以在如下网站 xff1a
TVS管与ESD保护二极管的区别
TVS管与ESD保护二极管的区别 ESD 静电放电 xff08 Electro Static discharge xff09 TVS 瞬变电压抑制二极管 xff08 Transient Voltage Suppressors xff09 T
Linux下使用C++连接MySql8.0一二三事
一 连接前准备 1 正确安装数据库 MySql 8 0 官网下载对应自己操作系统以及版本的包即可 xff0c 这一步一定要正确安装 推荐在官网https dev mysql com downloads 下载包 xff0c 之后使用 dpkg
操作系统 基础篇
基础篇 注 xff1a 本文内容总结自 计算机操作系统 第四版 xff08 汤小丹等人编著 xff09 第一章 引论 1 1 什么是操作系统 xff1f 操作系统是配置在计算机硬件上的第一层软件 其作用是 xff1a 管理好硬件设备 xff
正确地写出二分查找代码 确定二分查找的边界
一 Basic 二分查找一般用于在有序数组中查找某个特定的值 一般来说 xff0c 它需要确定 low 与 high 用于限定每轮迭代的范围 xff0c 用 mid 位置处元素与 target 比较 xff0c 从而降低每轮迭代的搜索空间
C++ 在构造函数和析构函数中调用虚函数究竟会怎么样?
首先摘取一段来自 Effective C 43 43 的解释 xff1a 派生类对象构造期间进入基类的构造函数时 xff0c 对象类型变成了基类类型 xff0c 而不是派生类类型 同样 xff0c 进入基类析构函数时 xff0c 对象也是基
VS2015运行项目时提示:.exe不是内部或外部命令,也不是可运行程序或批处理文件
VS2015运行项目时提示 xff1a exe不是内部或外部命令 xff0c 也不是可运行程序或批处理文件 解决方法 xff08 1 xff09 添加系统变量 xff08 即 xff0c 将该exe文件所在目录添加到系统Path中 xff0
二维码分类
二维码分类 最近在做二维码解码的项目 用的是zxing库 43 VS2015 43 OPENCV3 4 xff0c 网上说ZBAR库也ok xff1b 概念 xff1a 二维条码 二维码 xff08 2 dimensional bar co
Labview 编写TCP/IP 客户端断线重连机制程序,亲测可用
程序面板如下图 xff1a 此程序支持任意一方断线重连机制 xff0c 仅供大家参考 xff01 实际工程中 xff0c 如果出现服务器出现宕机 xff0c 那么我们的客户端要有重连的机制 xff0c 不然软件不会自动连接服务器 xff0c
C++ STL 库函数大全
include lt assert h gt 设定插入点 include lt ctype h gt 字符处理 include lt errno h gt 定义错误码 include lt float h gt 浮点数处理 include
多旋翼飞行器电机旋转方向图示
FreeRTOS浅析:解决两个任务运行冲突,系统停止一个任务唤醒另一个任务的方法
FreeRTOS中的任务和多线程的概念差不多 xff0c 但是任务的本质是把时间片无限的切小 xff0c 小到人分辨不出来 xff0c 其实还是一个时间只能运行一个任务 xff0c 这是和多线程的根本区别 FreeRTOS中的任务有几种运行
各种路由器接口与连接方法
转自于 http bbs pcsoft com cn thread 138952 1 4 html 路由器所在的网络位置比较复杂 xff0c 既可是内部子网边缘 xff0c 也可位于内 外部网络边缘 同时为了实现强大的适用性 xff0c 它
迭代器(Iterator)
迭代器 Iterator 是一个对象 xff0c 它的工作是遍历并选择序列中的对象 xff0c 它提供了一种访问一个容器 container 对象中的各个元素 xff0c 而又不必暴露该对象内部细节的方法 通过迭代器 xff0c 开发人员不
随机推荐
单片机串口发送数据很慢?这种方法帮助你提高!
本文介绍如何使用带FIFO的串口来减少接收中断次数 xff0c 通过一种自定义通讯协议格式 xff0c 给出帧打包方法 xff1b 之后介绍一种特殊的串口数据发送方法 xff0c 可在避免使用串口发送中断的情况下 xff0c 提高系统的响应
2020-11-21
xftp 提示无法显示远程文件夹 不是什么被动不被动的问题 是权限的问题 xff0c 如果这个文件夹有 34 x 34 权限 就可以打开 没有就不行
three.js加载3D模型(glb/gltf/fbx)
three js加载3D模型 glb gltf fbx 一 理解three 1 一个可以在某个3D建模软件打开的东西 xff0c 通过某种方案在浏览器中打开 xff1b 2 不要试图手动去创建3D图形 xff0c 当然比较闲的话可以这样操作
单片机复位电路原理
单片机的复位引脚RST 全称RESET 出现2个机器周期以上的复位电平 时 xff0c 单片机就执行复位操作 如果RST持续为复位电平 xff0c 单片机就处于循环复位状态 当单片机处于正常电平时就正常转入执行程序 图1 xff1a 当单片
在ubuntu下安装vmware-tools
用vmware虚拟机安装了ubuntu之后 xff0c 为了实现更加强大的功能 xff0c 比如说直接从windows主机拖文件进入ubuntu xff0c 以及加强ubuntu的性能 xff0c 我们一般都要安装vmware tools
虚拟机安装Ubantu 16.04,并修改配置文件更改网络配置
https blog csdn net qq 41016818 article details 81211744 ops request misc 61 amp request id 61 amp biz id 61 102 amp utm
Ubuntu 16.04下安装visual studio code
一 坑和解决办法 很多帖子上写的方法都是使用命令方式 xff1a 1 先安装make sudo add apt repository ppa ubuntu desktop ubuntu make sudo apt get update su
虚拟机Ubuntu与外网连接
详情可参考 xff1a https blog csdn net gaoganghua article details 80386107 ops request misc 61 257B 2522request 255Fid 2522 253
Xftp6如何连接虚拟机(Ubuntu)Windows与虚拟机之间传输文件
一 安装Ubuntu ssh出现异常 Err 1 http security ubuntu com ubuntu xenial security main amd64 openssh sftp server amd64 1 7 2p2 4u
ubuntu下搭建ftp服务器
1 安装vsftpd xff0c 安装命令 xff1a sudo apt get install vsftpd 查看是否安装成功 xff1a vsftpd version 2 新建一个文件夹用于FTP的工作目录 xff08 cpucard是
命名空间 std 中没有名为 stoi 的成员
我正在测试std stoi以下链接中的函数 xff1a 但我收到了错误 xff1a 已经添加了头文件 include lt string gt xff0c 但仍然错误提示 xff1a 在命名空间 std 中没有名为 stoi 的成员 xff
Linux+vscode 客户端通过代码操作远程服务器端数据库(MySQL)
这篇文章解决两个问题 xff1a 1 在Linux系统下使用vscode用C C 43 开发客户端程序时 xff0c 如何调用mysql库函数 xff1b 2 客户端与远程服务器端的MySQL连接时 xff0c 需要做哪些前期准备工作 xf
put操作提示 No such file or directory
https blog csdn net weixin 33875839 article details 86128344
IDEA导入lib目录下的jar包
https blog csdn net u010286027 article details 85248719 ops request misc 61 amp request id 61 amp biz id 61 102 amp utm
MIPS、ARM、X86三大架构
MIPS ARM X86三大架构 RISC平台的发展已经有长达几十年的历史了 其最早诞生于80年代的MIPS主机 xff0c 随着技术的不断发展 xff0c RISC平台的应用领域逐步扩展 xff0c 小到手机 xff0c 大到工控设备都可
报错:Diamond types are not supported at language level ‘6‘
在编译时报错 xff1a 这主要是1 6版本的javac exe编译器不支持菱形运算符 xff1b 解决办法 xff1a 1 修改设置settings和项目结构Project Structure中的JDK版本设置 配置IDEA编译器版本 2
Ubuntu18.04.3虚拟机安装步骤
Ubuntu18 04 3虚拟机安装步骤 xff08 图文教程 xff0c 非常详细 xff01 xff01 xff01 xff09 丶无殇的博客 CSDN博客 ubuntu18虚拟机安装
信号包络
将一段时间长度的高频信号的峰值点连线 xff0c 就可以得到上方 xff08 正的 xff09 一条线和下方 xff08 负的 xff09 一条线 xff0c 这两条线就叫包络线 包络线就是反映高频信号幅度变化的曲线 对于等幅高频信号 xf
归一化函数normalize详解
opencv 2 归一化函数normalize详解 1 归一化定义与作用 归一化 就是要把需要处理的数据经过处理后 xff08 通过某种算法 xff09 限制在你需要的一定范围内 首先归一化是为了后面数据处理的方便 xff0c 其次是保证程
多态性之编译期多态和运行期多态(C++版)
多态性之编译期多态和运行期多态 C 43 43 版 C 43 43 中最为经典的就是多态性 xff0c 多态性充分体现了面向对象的思想 xff0c 并且是C 43 43 与C的最大区别之一 多态性分为编译期多态和运行期多态 xff0c 也称
热门标签
dart2
tchar
quickblox
omnifaces
skflow
dtls
forceclose
xlink
obr
kcfinder
androidsql
textsize
uniscribe