第十一章 多态

        多态是面向对象开发过程中一个非常重要的概念。

11.1 多态概述

11.1.1 什么是多态

        多态(polymorphism),从字面理解是“多种形态,多种形式”,是一种将不同的特殊行为泛化为当个特殊记号的机制。

多态从实现的角度可划分为两类:

(1)编译时的多态:编译过程中确定了同名操作的具体操作对象

(2)运行时的多态:程序运行时才动态确定操作所针对的具体对象

11.1.2  多态的引入

先尝试理解以下程序:

#include<iostream>
using namespace std;class Animal
{public:void sleep(){cout<<"Animal sleep"<<endl;}void breathe(){cout<<"Animal breath"<<endl;}
};
class Fish:public Animal
{public:void breathe(){cout<<"Fish double"<<endl;}
};
int main()
{Fish fh;Animal *an=&fh;an->breathe();return 0;
}

运行结果:

分析过程如下:

如何解决这个问题呢?可以通过多态来解决。

11.1.3 联编

联编是确定操作的具体对象的过程,只计算机程序自身彼此关联的过程,即把一个标识符名和一个存储地址联系在一起的过程

根据进行阶段的不同,可以分为静态联编与动态联编,这两种联编过程分别对应多态的两种实现方式,如下图:

11.2 函数重载

        函数重载与运算符重载可以实现编译时的多态性。以下只介绍函数重载,运算符重载见第12章。

        函数重载也称为多态函数

(1)作用:使程序能够用同一个名字来访问一组相关的函数,提高程序灵活性。

(2)含义:函数名相同,但函数所带的参数个数或数据类型不同(编译系统会根据参数来决定调用哪个同名函数)        

        函数重载表现为两种情况:

(1)参数个数或类型有所差别的重载

(2)函数的参数完全相同但属于不同类

第一种情况与构造函数重载类似,不作追诉,只介绍第二种情况。当函数的参数完全相同但属于不同类时,为了让编译能正确区分调用哪个类的同名函数,可用以下两种方法别:

(1)用对象名区别,在函数名前加上对象名来限制对象名.函数名

(2)用类名和作用域运算符区别,在函数名前加“类名::”来限制(类名::函数名

示例代码:

#include<iostream>
using namespace std;class Point
{int x,y;public:Point(int a,int b){x=a;y=b;}double area(){return x*y;}};
class Circle:public Point
{int r;public:Circle(int a,int b,int c):Point(a,b){r=c;}double area(){return 3.1416*r*r;}
};
int main()
{Point pob(15,15);Circle cob(20,20,10);cout<<"pob.area()="<<pob.area()<<endl<<endl;cout<<"cob.area()"<<cob.area()<<endl<<endl;cout<<"cob.Point::area()"<<cob.Point::area()<<endl<<endl;return 0;
}

运行结果:

通过函数重载方式,可以是同名函数实现不同功能,即实现了静态多态性

11.3 虚函数

         虚函数是实现运行时多态的一个重要方式,是重载的另一种形式。也就是动态编联。

11.3.1 定义虚函数

        虚函数的定义是在基类中进行的,即把基类中需要定义为虚函数的成员函数声明为virtual。虚函数定义的一般形式如下:

virtual <函数类型><函数名>(参数表)
{函数体;
}
virtual void breathe()
{cout<<"Animal breathe"<<endl;
}

        当基类中的某个成员函数被声明为虚函数后,就可以在派生类中重新定义。在派生类中重新定义时,其函数原型包括返回类型、函数名、参数个数和类型,参数的顺序都必须与基类中的原型完全一致

示例代码:(与第一个代码做对比)

#include<iostream>
using namespace std;class Animal
{public:void sleep(){cout<<"Animal sleep"<<endl;}virtual void breathe(){cout<<"Animal breath"<<endl;}
};
class Fish:public Animal
{public:void breathe(){cout<<"Fish double"<<endl;}
};
int main()
{Fish fh;Animal *an=&fh;an->breathe();return 0;
}

运行结果:

代码将基类中的成员函数breathe()定义为虚函数,即加上virtual关键字,然后在主函数main()中定义Animal对象指针指向Fish的对象fh,调用breathe()函数后,得到的结果就是预期输出Fish bubble的结果。具体分析过程如下:

在使用派生类对象指针时应该注意:

(1)不能指向私有派生类的对象;

(2)指向公有派生类对象时,只能访问派生类中从基类继承下来的成员,不能直接访问公有派生类中定义的成员。

(3)指向派生类对象的指针不能指向基类的对象;

虚函数可以很好的实现多态,在使用虚函数是应注意的问题如下:

(1)虚函数的声明只能出现在类函数原型的声明中,不能出现在函数体实现时;

(2)基类中只有保护成员或公有成员才能被声明为虚函数

(3)在派生类中重新定义虚函数时,关键字virtual可以写也可以不写,但在容易引起混乱时,应该写上关键字;

(4)动态编联只能通过成员函数来调用或通过指针、引用来访问虚函数

在派生类中程序定义基类中的虚函数,是函数重载的另一种形式,但它与函数重载又有区别:

(1)一般函数重载,要求其函数的参数数量或参数类型必须有所不同,函数的返回值也可以不同;

(2)重载一个虚函数时,要求函数名、返回类型、参数个数、参数的类型和参数的顺序必须与基类中的虚函数的原型完全相同

11.3.2 多级继承和虚函数

        多级继承可以看作是多个单继承的组合,多级继承的虚函数与单继承的虚函数的调用相同。即不同类创建的对象调用的函数是不一样的。

示例代码:

#include<iostream>
using namespace std;class Base
{public:virtual void func(){cout<<"Base output"<<endl;}};
class Derived1:public Base
{public:void func(){cout<<"Derived1 output"<<endl;}
};
class Derived2:public Derived1
{public:void func(){cout<<"Derived2 output"<<endl;}
};
void test(Base &b)
{b.func();
}int main()
{Base bObj;Derived1 d1Obj;Derived2 d2Obj;test(bObj);cout<<endl;test(d1Obj);cout<<endl;test(d2Obj);cout<<endl;return 0;
}

运行结果:

        上述代码中定义了一个多级继承,在基类中定义了虚函数func(),在主函数main()中调用该函数时,不同类创建的对象调用的函数是不一样的,即实现了多态的“一个接口,多种实现”。

11.4 纯虚函数与抽象类

        抽象类是一种包含纯虚函数的特殊类。建立抽象类时为了多态地使用抽象类的成员函数。

11.4.1 纯虚函数

        在基类中不能为虚函数给出一个有意义的实现时,可以将其声明为纯虚函数。纯虚函数的实现可以留给派生类来完成。纯虚函数的作用是为派生类提供一个一致的接口。一般来说,一个抽象类带有至少一个纯虚函数。纯虚函数的一般定义如下:

        纯虚函数与普通函数定义的不同在于书写形式加了“=0”,说明在基类中不用定义改函数的函数体。

示例代码:纯虚函数的函数体由派生类定义;

#include<iostream>
using namespace std;class Point
{protected:int x0,y0;public:Point(int i=0,int j=0){x0=i;y0=j;                }    virtual void set()=0;//声明纯虚函数
};
class Line:public Point
{protected:int x1,y1;public:Line(int i=0,int j=0,int m=0,int n=0):Point(i,j){x1=m;y1=j;}void set()//定义接口函数{cout<<"Line::set() called \n";}
};void setobj(Point *p)
{p->set();
}int main()
{Line *lineobj = new Line;setobj(lineobj);return 0;
}

运行结果:

11.4.2 抽象类

        抽象类是包含纯虚函数的一种特殊类,是为了抽象和设计而建立的,处于继承层次结果的教上层。抽象类是不能建立对象的,为了强调一个类是抽象类,可讲该类的构造函数声明为保护的访问控制权限。抽象类的主要作用如下:

(1)将有关的类组织在一个继承层层次结构中,由抽象类来为它们提供一个公共的根,相关的子类是从这个根派生出来的。

(2)抽象类只描述这组子类沟通的操作接口,而完整的实现留给子类

使用抽象类是应注意:

(1)抽象类只能用做其他类的基类,不能创建抽象类的对象

(2)抽象类不能用做参数类型、函数的返回类型或显示转换的类型

(3)可以声明抽象类的对象指针或对象引用,从而可以访问派生类对象成员,实现多态编联

(4)若派生类中没有给出抽象类的所有纯虚函数的函数体,派生类仍然是一个抽象类,若派生类中给出抽象类的所有纯虚函数的函数体,则这个派生类不再是抽象类,可以创建自己的对象。

示例代码:

#include<iostream>
using namespace std;class Vehicle
{protected:float speed;int total;public:Vehicle(float speed,int total){Vehicle::speed=speed;Vehicle::total=total;}virtual void ShowMember()=0;
};
class Car:public Vehicle
{protected:int aird;public:Car(int aird,float speed,int total):Vehicle(speed,total){Car::aird=aird;}void ShowMember(){cout<<"the speed is:"<<speed<<endl;cout<<"the total is:"<<total<<endl;cout<<"the aird is:"<<aird<<endl;}
};int main()
{//Vehicle a(100,4);Car b(250,150,4);b.ShowMember();return 0;
}

运行结果:

声明抽象类Vehicle的对象a(100,4)时报错,不能为抽象类声明对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80577.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RNN——循环神经网络

一.基本结构 1.目标&#xff1a;处理序列数据&#xff08;时间序列&#xff0c;文本&#xff0c;语音等&#xff09;&#xff0c;捕捉时间维度上的依赖关系 核心机制&#xff1a;通过隐藏状态&#xff08;hidden State&#xff09;传递历史信息&#xff0c;每个时间步的输入包…

性能提升手段--池化技术

看到hadoop代码里有ByteBufferPool,使用池子来避免频繁创建、销毁ByteBuffer,减轻GC压力,提高性能。 顺便总结一下池化技术 一、什么是池化技术?​​ ​​池化(Pooling)​​ 是一种资源管理策略,通过​​预先创建并复用资源​​(如数据库连接、线程、内存对象等)来提…

数据安全和合规性市场分析

一、什么是数据安全和合规性 在数据安全和合规性方面&#xff0c;存在着一系列重要的法律、法规和行业标准&#xff0c;这些规定了组织如何收集、存储、处理和保护个人数据及其他敏感信息。企业之所以要遵守这些规定&#xff0c;是出于多方面的考量&#xff0c;既有法律责任&a…

【每日八股】复习计算机网络 Day4:TCP 协议的其他相关问题

文章目录 昨日内容复习已经建立了 TCP 连接&#xff0c;客户端突然出现故障怎么办&#xff1f;什么时候用长连接&#xff1f;短连接&#xff1f;TCP 的半连接队列与全连接队列&#xff1f;什么是 SYN 攻击&#xff1f;如何避免&#xff1f;TIME_WAIT 的作用&#xff1f;过多如何…

React:<></>的存在是为了什么

1. <></> 是什么&#xff1f; <></> 是 React 的Fragment&#xff08;片段&#xff09;语法糖&#xff0c;等价于 <React.Fragment></React.Fragment>。 2. 它的作用 主要作用&#xff1a; 允许你在组件里返回多个元素&#xff0c;而不需…

cron定时任务

cron定时任务 一、Cron表达式的定义 基础结构 Cron表达式是由空格分隔的6或7个字段组成的字符串&#xff0c;格式为&#xff1a; 秒 分 时 日 月 星期 [年]其中&#xff0c;年通常可以被省略 字段说明&#xff1a; 秒&#xff08;0-59&#xff09; 秒字段表示每分钟的哪一…

分布式之易混淆概念

昨天写UE写的破防了&#xff0c;忘了写文章&#xff0c;今天补一下分布式的一些概念。&#x1f61a; 在软件架构领域&#xff0c;微服务、领域驱动设计&#xff08;DDD&#xff09;和分布式系统是三个高频且容易被混淆的概念。许多开发者误以为它们是“同一件事的不同说法”&a…

量子跃迁:Vue组件安全工程的基因重组与生态免疫(完全体终局篇)

开篇数字免疫系统的范式革命 在2025年某国际金融峰会期间&#xff0c;黑客组织利用量子计算技术对全球37个交易系统发起协同攻击。传统安全组件在2.7秒内集体失效&#xff0c;造成每秒超18亿美元的交易漏洞。这场数字"切尔诺贝利"事件促使我们重新定义前端安全——组…

Operating System 实验七 Linux文件系统实验

实验目标: 使用dd命令创建磁盘镜像文件ext2.img并格式化为ext2文件系统,然后通过mount命令挂载到Linux主机文件系统。查看ext2文件系统的超级块的信息,以及数据块的数量、数据块的大小、inode个数、空闲数据块的数量等信息 在文件系统中创建文件xxxxx.txt(其中xxxxx为你的学…

模型识别能力锤炼及清单

大脑将注意力分配给需要消耗脑力的活动&#xff0c;通过学习技能&#xff0c;大脑也能更轻松的工作。这个时候&#xff0c;大脑负责管理注意力控制和努力控制的区域活动会大幅减少。沉浸式学习是学习一门新的语言的最佳方式&#xff0c;也是深入洞察错综复杂商业环境的绝佳途径…

Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案

整个应用&#xff08;包括 View 和 Compose 部分&#xff09;的浅色/深色模式保持一致。以下是完整的解决方案&#xff1a; 全局配置方案 1. 基础主题设置 在 res/values/themes.xml 和 res/values-night/themes.xml 中定义统一的主题&#xff1a; <!-- values/themes.x…

QT开发技术【QT实现桌面右下角消息】

一、效果 ![ 二、弹窗主体部分 noticewidget /* ** File name: NoticeWidget.h ** Author: ** Date: 2025-04-25 ** Brief: 通知栏控件 ** Copyright (C) 1392019713qq.com All rights reserved. */#include "../Include/NoticeWidget.h"…

在LiveGBS GB28181互联网安防监控平台中关于redis版本切换的方法说明

目录 1、Redis服务2、如何切换REDIS? 2.1、停止启动REDIS2.2、配置信令服务2.3、配置流媒体服务2.4、启动3、搭建GB28181视频直播平台 1、Redis服务 在LivGBS中Redis作为数据交换、数据订阅、数据发布的高速缓存服务。默认LiveCMS解压目录下会携带一个REDIS服务。如果已经有自…

vue3中的effectScope有什么作用,如何使用?如何自动清理

vue3中的effectScope有什么作用&#xff0c;如何使用?如何自动清理 vue3中的effectScope有什么作用&#xff0c;如何使用 官网介绍&#xff1a;作用特点简单示例&#xff1a;自动清理示例 官网介绍&#xff1a; 创建一个 effect 作用域&#xff0c;可以捕获其中所创建的响应…

搭建基于火灾风险预测与防范的消防安全科普小程序

基于微信小程序的消防安全科普互动平台的设计与实现&#xff0c;是关于微信小程序的&#xff0c;知识课程学习&#xff0c;包括学习后答题。 技术栈主要采用微信小程序云开发&#xff0c;有下面的模块&#xff1a; 1.课程学习模块 2.资讯模块 3.答题模块 4.我的模块 还需…

python 与Redis操作整理

以下是使用 Python 操作 Redis 的完整整理&#xff0c;涵盖基础操作、高级功能及最佳实践&#xff1a; 1. 安装与连接 (1) 安装库 pip install redis(2) 基础连接 import redis# 创建连接池&#xff08;推荐复用连接&#xff09; pool redis.ConnectionPool(hostlocalhost, …

什么时候使用Python 虚拟环境(venv)而不用conda

是的&#xff01;python3.9 -m venv rtdetr_env 是 Python 原生的虚拟环境&#xff08;venv&#xff09;&#xff0c;而 conda 是另一个流行的虚拟环境管理工具&#xff08;来自 Anaconda/Miniconda&#xff09;。下面我会详细对比两者的区别&#xff0c;并讲解 venv 的基本用法…

ubuntu20.04安装x11vnc远程桌面

x11vnc是一个VNC服务器, 安装后我们可以不依赖外部的显示设备, 通过网络远程登录ubuntu桌面。 安装x11vnc sudo apt-get install x11vnc 设置VNC登录密码 sudo x11vnc -storepasswd /etc/x11vnc.pwd 设置x11vnc在开机时自动启动 新建如下文件: sudo vi /lib/systemd/sys…

Maven的概念与初识Maven

目录 一、Maven的概念 1. 什么是Maven 2. 项目构建&#xff1a;从代码到部署的标准化流程 2.1 Maven构建生命周期 2.2 传统构建 vs Maven构建 3. 依赖管理&#xff1a;解决“JAR地狱”的利器 3.1 依赖声明 3.2 依赖传递与冲突解决 4. Maven仓库&#xff1a;依赖的存储…

Unity-Shader详解-其二

前向渲染和延迟渲染 前向渲染和延迟渲染总的来说是我们的两种主要的渲染方式。 我们在Unity的Project Settings中的Graphic界面能够找到渲染队列的设定&#xff1a; 我们也可以在Main Camera这里进行设置&#xff1a; 那这里我们首先介绍一下两种渲染&#xff08;Forward R…