当前位置: 首页 > news >正文

面向对象之_多态_1

目录

一. 多态

多态是什么

二. 多态的构成条件

1. 虚函数

2. 虚函数重写(隐藏)

3. 父类型的引用或者指针调用

 4. 多态的特殊情况

1) 子类可以不加 virtual 关键字

2) 协变

三. 关键字

1.  virtual 

2. final

3. override

四. 多态的原理

1. 虚函数表指针

2. 调用原理

多态是怎么样被调用的?

多态为什么不能是父类的对象调用?

为什么需要是虚函数呢?

五. 总结


一. 多态

多态是什么

多态:简单的说就是不同类型的对象调用相同的函数产生的结果是不同的。

二. 多态的构成条件

  1. 虚函数重写

  2. 父类型的指针或者引用调用虚函数

1. 虚函数

虚函数就是一个函数前面加一个关键字 virtual 关键字,这样就是虚函数了,但是虚函数只能是成员函数才能是虚函数,而全局函数不可以成为虚函数。

virtual void fun()
{cout << "fun()" << endl;
}int main()
{return 0;
}

只有成员函数才可以是虚函数。

class A
{
public:virtual void fun(){}
};

 这样就是没有问题的。

2. 虚函数重写(隐藏)

我们的父类和子类只有函数三同才可以构成虚函数重写。

三同:函数名相同,参数相同,返回值相同,然后还需要是虚函数。

只有满足三同并且还都是虚函数才能构成重写。

class person
{
public:virtual void buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual void buyTicket(){cout << "void student::buyTicket()" << endl;}
};

上面代码就是一个重写。

3. 父类型的引用或者指针调用

void fun(person& p)
{p.buyTicket();
}int main()
{person p;student s;fun(p);fun(s);return 0;
}

上面就是多态了。

如果我们破坏了里面的任意一个条件,那么就是不构成多态的,我们下面破坏掉虚函数的条件,让父类不是虚函数。

class person
{
public:void buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual void buyTicket(){cout << "void student::buyTicket()" << endl;}
};void fun(person& p)
{p.buyTicket();
}int main()
{person p;student s;fun(p);fun(s);return 0;
}

 

此时我们的调用就是普通的 person对象 调用 buyTicket 函数。

我们在破坏一个其他的条件,返回值。

class student : public person
{
public:virtual void buyTicket(){cout << "void student::buyTicket()" << endl;}
};void fun(person& p)
{p.buyTicket();
}

 此时编译都不通过。

我们在破坏一个参数的条件。

class person
{
public:virtual void buyTicket(int a){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual void buyTicket(){cout << "void student::buyTicket()" << endl;}
};

 此时编译也是不通过的,如果改变的是子类的参数,那么编译通过但是调用时不成功的。

 4. 多态的特殊情况

在多态里面,是由一些特殊情况的

1) 子类可以不加 virtual 关键字

class person
{
public:virtual void buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:void buyTicket(){cout << "void student::buyTicket()" << endl;}
};

我们的多态也是成功的,所以在多态这里我们的子类时可以不加 virtual 关键字的,但是基类时需要加的。

2) 协变

 协变就是参数可以不同,但是我们是有条件的,我们的协变只能是返回父类和子类的指针或者是引用。

class person
{
public:virtual person& buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual student& buyTicket(){cout << "void student::buyTicket()" << endl;}
};

而且我们必选是同时是指针或者是引用,我们也不仅是可以是自己的父类和子类,我们也可以是其他的父类和子类。

class A
{};class B : public A
{};class person
{
public:virtual A& buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual B& buyTicket(){cout << "void student::buyTicket()" << endl;}
};

而我们的返回值必须是父类返回父类,子类返回子类。

class person
{
public:virtual B& buyTicket(){cout << "void person::buyTicket()" << endl;}
};class student : public person
{
public:virtual A& buyTicket(){cout << "void student::buyTicket()" << endl;}
};

三. 关键字

1.  virtual 

virtual 关键字:

  • 虚继承:在继承的时候使用该关键字
  • 虚函数:在函数前面加 virtual 关键字

2. final

final 关键字:

  • 最终类:在类的后面加 final 表示该类无法被继承
class A final
{};class B : public A
{};

这里就是我们不能对 A 进行继承

3. override

override 关键字:

  • 检查是否完成重写:在派生类中使用,加在想要被重写的函数后面
  • 静态检查:在编译时静态检查,确保派生类中重写了基类的虚函数
class A 
{
public:void fun(){}
};class B : public A
{
public:void fun() override{}
};

 

 许哦一这里只需要完成重写就可以了。

class A 
{
public:virtual void fun(){}
};class B : public A
{
public:void fun() override{}
};

四. 多态的原理

1. 虚函数表指针

在前面的菱形虚拟继承继承中,如果有重定义的函数或者变量,那么里面是有一个虚基表的,这个是虚函数表指针,不要弄混了,下面看一下虚函数表指针。

在有虚函数的类中,该类都会有一个虚函数表指针,该指针就是指向该类中的虚函数的。

class A
{
public:virtual void fun(){}int _a;
};int main()
{A a;cout << sizeof(A) << endl;return 0;
}

这里的 sizeof 是8,所以这里的 A 肯定不只有一个 _a 的成员变量,还有一个虚函数表指针,这个是一个指针,所以在 32 位和 64 位下的环境是不同的。

下面1看一下该类的内部是怎么样的。

 

看一下这个指针里面的内容。

 

现在 a 这个对象里面只有一个虚函数,所以该表里面只存储一个,而虚函数表指针的本质,就可以理解为一个函数指针数组,该函数指针数组里面存储的就是一个个虚函数,如果还有其他的虚函数,那么还是会放在该类的这个虚函数表里面。

虚函数表:每个类共同使用一个虚函数表。

下面看一下相同类型的对象使用的虚函数表是否相同。

 

 

2. 调用原理

多态是怎么样被调用的?

多态是需要父类的对象或者是指针,然后调用重写的虚函数。

在父类的指针或者引用看来,它自己指向的对象模型就是一个父类对象。

class A
{
public:virtual void fun(){cout << "A::fun()" << endl;}int _a;
};class B : public A
{
public:virtual void fun(){cout << "B::fun()" << endl;}int _b;
};int main()
{B b;return 0;
}

这里看一下B 类型对象的对象模型

 所以在父类的引用或者指针看来就是一个父类的对象,但是调用为什么会形成多态?

看一下这个虚表指针里面的内容。

B 类型的对象里面的虚表指针里面是重写后的自己的虚函数,所以在调用的时候可以调用到自己的函数。

那么为什么B 类型的对象里面的虚表指针里面是自己的函数地址呢?下面分为3步。

  1. 在多态中,派生类首先会把基类的虚函数表拷贝下来。
  2. 如果派生类中对基类的虚函数进行了重写,那么就会将虚函数表中的基类的被重写的虚函数替换成自己的虚函数的地址。
  3. 如果自己也有自己的虚函数,就将自己的虚函数添加到虚函数表中。

 

多态为什么不能是父类的对象调用?

多态为什么不能是父类的对象呢?

int main()
{B b;A a = b;
}

现在将b 赋值给a,那么现在的a里面的虚表是父类的虚表还是子类的虚表?

当然是自己的,因为在父子类在进行赋值的时候,虚表是不会进行赋值的,如果虚表进行赋值的话,那么现在的 a 里面的虚表就是B 类型的虚表,那么a 对象调用虚函数的时候是调用的就是派生类的函数了,所以在进行赋值的时候虚表不能赋值,由于虚表在赋值的时候不能不拷贝,所以多态调用的时候不能是父类的对象。

int main()
{B b;A a;b._a = 10;a = b;
}

通过这段代码测试一下,这里将里面的 _a 变量赋值时为例更好的查看。

 

这里看到虚表时没有变化的。

为什么需要是虚函数呢?

前面说过了,虚函数会放到虚表里面,那么如果不是虚函数的话,那么当然是不会放在虚表里面的,所以如果函数都不在虚表里面即使是父类的指针或者是引用在虚表里面也找不到应该重写后的虚函数,所以也就没法实现多态。

五. 总结

多态

  • 多态的调用需要父类的引用或者是指针调用
  • 调用的函数必须是虚函数进行重写
  • 虚函数重写的条件是三同(函数名相同,返回值,参数)
  • 在多态这里子类是可以不加 virtual 的,只要父类加了,那么子类与父类的函数又是三同,就构成多态
  • 协变,协变就是返回值可以不同,但是返回值必须是父类返回父类的指针或者是引用,子类必须返回子类的指针或者是引用,而且返回的父子类只要是父子类既可以,不一定是自己的父子类 

相关文章:

面向对象之_多态_1

目录 一. 多态 多态是什么 二. 多态的构成条件 1. 虚函数 2. 虚函数重写&#xff08;隐藏&#xff09; 3. 父类型的引用或者指针调用 4. 多态的特殊情况 1) 子类可以不加 virtual 关键字 2) 协变 三. 关键字 1. virtual 2. final 3. override 四. 多态的原理 1. 虚…...

Spring学习笔记之spring概述

文章目录 Spring介绍Spring8大模块Spring特点 Spring介绍 Spring是一个轻量级的控制反转和面向切面的容器框架 Spring最初的出现是为了解决EJB臃肿的设计&#xff0c;以及难以测试等问题。 Spring为了简化开发而生&#xff0c;让程序员只需关注核心业务的实现&#xff0c;尽…...

旧项目导入Eclipse时文件夹看起来乱七八糟,无从下手的解决办法(无main或webapp等文件夹)

首先&#xff0c;如果没有main或java/resource/webapp等文件夹&#xff0c;那就自己在src下面创建一个&#xff0c;只要对应关系与我下图左边红框一致即可&#xff0c;创建完之后java文件移到java文件夹下&#xff0c;资源文件例如.properties、老项目的数据源定义.INI文件、日…...

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】

Reinforcement Learning with Code 【Code 2. Tabular Sarsa】 This note records how the author begin to learn RL. Both theoretical understanding and code practice are presented. Many material are referenced such as ZhaoShiyu’s Mathematical Foundation of Rei…...

服务调用---------Ribbon和Feign

目录​​​​​​​ 1、Ribbon 1.1 Ribbon简介 1.2 Ribbon负载均衡 负载均衡原理 负载均衡策略 Ribbon和Nginx的区别 1.3 服务调用和Ribbon负载均衡实现 2、Feign&openFeign 3、Feign支持的配置 日志功能 连接池 feign-api远程包 1、Ribbon 1.1 Ribbon简介 Ribb…...

app自动化测试之Appium问题分析及定位

使用 Appium 进行测试时&#xff0c;会产生大量日志&#xff0c;一旦运行过程中遇到报错&#xff0c;可以通过 Appium 服务端的日志以及客户端的日志分析排查问题。 Appium Server日志-开启服务 通过命令行的方式启动 Appium Server&#xff0c;下面来分析一下启动日志&#…...

婚庆服务小程序app开发方案详解

开发一款婚庆行业服务小程序有哪些功能呢&#xff1f; 1、选择分类 选择婚庆、婚车、婚宴、司仪、彩妆、婚庆用品、跟拍、摄影等&#xff0c;筛选出对应的商家 2、选择商家 选择分类后&#xff0c;可以选择商家&#xff0c;查看各个商家的详细介绍情况。 3、选择服务套餐 各…...

集合简述

集合ListArrayListLinkedList SetHashSetTreeSet MapHashMapTreeMap 集合与数组的区别 集合 集合是java中的一个容器&#xff0c;可以在里面存放数据&#xff0c;容量可以发生改变 从集合框架结构可以分析得知&#xff1a; 1、集合主要分为Collection和Map两个接口 2、Collecti…...

常见的软件测试面试题汇总

一、 你们的测试流程是怎么样的&#xff1f; 答&#xff1a;1.项目开始阶段&#xff0c;BA&#xff08;需求分析师&#xff09;从用户方收集需求并将需求转化为规格说明书&#xff0c;接 下来在项目组领导会组织需求评审。 2.需求评审通过后&#xff0c;BA 会组织项目经理…...

学习笔记|大模型优质Prompt开发与应用课(二)|第二节:超高产文本生成机,传媒营销人必备神器

文章目录 01 文字写作技能的革新&#xff0c;各行各业新机遇四大类常见文字工作新闻记者的一天新闻记者的一天–写策划prompt 新闻记者的一天–排采访prompt生成结果prompt生成结果 大模型加持&#xff0c;文字写作我们如何提效营销创作营销创作-使用预置法为不同平台生成文案p…...

Linux基础-4

1、linux高阶命令 1.1、find 在linux文件系统中&#xff0c;用来查找一个文件放在哪里了。 //举例 find /etc -name "interfaces" //总结&#xff1a; //(1)什么时候用find&#xff1f; //当你知道你要找的文件名&#xff0c;但是你忘记了它被放在哪个目录下&…...

oracle-创建函数

oracle自定义函数 核心提示&#xff1a;函数用于返回特定数据。执行时得找一个变量接收函数的返回值; 语法如下: create or replace function function_name ( argu1 [mode1]datatype1, argu2 [mode2] datatype2, … ) return datatype is begin end; 执行 var v1 varchar2(1…...

【Ansible 的脚本 --- playbook 剧本】

目录 一、playbook 剧本介绍二、示例1、运行playbook2、定义、引用变量 三、使用playbook部署lnmp集群 一、playbook 剧本介绍 playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在…...

ubuntu释放缓存

sudo sysctl vm.drop_caches1 sudo sysctl vm.drop_caches2 sudo sysctl vm.drop_caches3释放页面缓存&#xff1a; $ sudo sysctl vm.drop_caches1释放目录项和索引节点缓存&#xff1a; $ sudo sysctl vm.drop_caches2释放页面缓存、目录项和索引节点缓存&#xff1a; $ sudo…...

实用调试技巧(1)

什么是bug&#xff1f;调试是什么&#xff1f;有多重要&#xff1f;debug和release的介绍。windows环境调试介绍。一些调试的实例。如何写出好&#xff08;易于调试&#xff09;的代码。编程常见的错误。 什么是Bug 我们在写代码的时候遇到的一些问题而导致程序出问题的就是Bu…...

uniapp:H5定位当前省市区街道信息

高德地图api&#xff0c;H5定位省市区街道信息。 由于uniapp的uni.getLocation在H5不能获取到省市区街道信息&#xff0c;所以这里使用高德的逆地理编码接口地址接口&#xff0c;通过传key和当前经纬度&#xff0c;获取到省市区街道数据。 这里需要注意的是&#xff1a;**高德…...

自然语言处理从入门到应用——LangChain:提示(Prompts)-[提示模板:部分填充的提示模板和提示合成]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 部分填充的提示模板 提示模板是一个具有.format方法的类&#xff0c;它接受一个键值映射并返回一个字符串&#xff08;一个提示&#xff09;&#xff0c;以传递给语言模型。与其他方法一样&#xff0c;将提示模板进行…...

论文笔记--GloVe: Global Vectors for Word Representation

论文笔记--GloVe: Global Vectors for Word Representation 1. 文章简介2. 文章概括3 文章重点技术3.1 两种常用的单词向量训练方法3.2 GloVe3.3 模型的复杂度 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;GloVe: Global Vectors for Word Representa…...

day57|● 647. 回文子串 ● 516.最长回文子序列

647. 回文子串 https://leetcode.cn/problems/palindromic-substrings/solution/by-lfool-2mvg/ Given a string s, return the number of palindromic substrings in it. A string is a palindrome when it reads the same backward as forward. A substring is a contiguous…...

docker compose.yml学习

docker compose 安装docker-compose sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composechmod x /usr/local/bin/docker-composeln -s /usr/local/bin/docker-…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

大数据零基础学习day1之环境准备和大数据初步理解

学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 &#xff08;1&#xff09;设置网关 打开VMware虚拟机&#xff0c;点击编辑…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...