面向对象之_多态_1
目录
一. 多态
多态是什么
二. 多态的构成条件
1. 虚函数
2. 虚函数重写(隐藏)
3. 父类型的引用或者指针调用
4. 多态的特殊情况
1) 子类可以不加 virtual 关键字
2) 协变
三. 关键字
1. virtual
2. final
3. override
四. 多态的原理
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步。
- 在多态中,派生类首先会把基类的虚函数表拷贝下来。
- 如果派生类中对基类的虚函数进行了重写,那么就会将虚函数表中的基类的被重写的虚函数替换成自己的虚函数的地址。
- 如果自己也有自己的虚函数,就将自己的虚函数添加到虚函数表中。
多态为什么不能是父类的对象调用?
多态为什么不能是父类的对象呢?
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. 虚函数重写(隐藏) 3. 父类型的引用或者指针调用 4. 多态的特殊情况 1) 子类可以不加 virtual 关键字 2) 协变 三. 关键字 1. virtual 2. final 3. override 四. 多态的原理 1. 虚…...

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

旧项目导入Eclipse时文件夹看起来乱七八糟,无从下手的解决办法(无main或webapp等文件夹)
首先,如果没有main或java/resource/webapp等文件夹,那就自己在src下面创建一个,只要对应关系与我下图左边红框一致即可,创建完之后java文件移到java文件夹下,资源文件例如.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 进行测试时,会产生大量日志,一旦运行过程中遇到报错,可以通过 Appium 服务端的日志以及客户端的日志分析排查问题。 Appium Server日志-开启服务 通过命令行的方式启动 Appium Server,下面来分析一下启动日志&#…...

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

集合简述
集合ListArrayListLinkedList SetHashSetTreeSet MapHashMapTreeMap 集合与数组的区别 集合 集合是java中的一个容器,可以在里面存放数据,容量可以发生改变 从集合框架结构可以分析得知: 1、集合主要分为Collection和Map两个接口 2、Collecti…...
常见的软件测试面试题汇总
一、 你们的测试流程是怎么样的? 答:1.项目开始阶段,BA(需求分析师)从用户方收集需求并将需求转化为规格说明书,接 下来在项目组领导会组织需求评审。 2.需求评审通过后,BA 会组织项目经理…...

学习笔记|大模型优质Prompt开发与应用课(二)|第二节:超高产文本生成机,传媒营销人必备神器
文章目录 01 文字写作技能的革新,各行各业新机遇四大类常见文字工作新闻记者的一天新闻记者的一天–写策划prompt 新闻记者的一天–排采访prompt生成结果prompt生成结果 大模型加持,文字写作我们如何提效营销创作营销创作-使用预置法为不同平台生成文案p…...
Linux基础-4
1、linux高阶命令 1.1、find 在linux文件系统中,用来查找一个文件放在哪里了。 //举例 find /etc -name "interfaces" //总结: //(1)什么时候用find? //当你知道你要找的文件名,但是你忘记了它被放在哪个目录下&…...
oracle-创建函数
oracle自定义函数 核心提示:函数用于返回特定数据。执行时得找一个变量接收函数的返回值; 语法如下: 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 本身由以下各部分组成 (1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在…...
ubuntu释放缓存
sudo sysctl vm.drop_caches1 sudo sysctl vm.drop_caches2 sudo sysctl vm.drop_caches3释放页面缓存: $ sudo sysctl vm.drop_caches1释放目录项和索引节点缓存: $ sudo sysctl vm.drop_caches2释放页面缓存、目录项和索引节点缓存: $ sudo…...

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

uniapp:H5定位当前省市区街道信息
高德地图api,H5定位省市区街道信息。 由于uniapp的uni.getLocation在H5不能获取到省市区街道信息,所以这里使用高德的逆地理编码接口地址接口,通过传key和当前经纬度,获取到省市区街道数据。 这里需要注意的是:**高德…...
自然语言处理从入门到应用——LangChain:提示(Prompts)-[提示模板:部分填充的提示模板和提示合成]
分类目录:《自然语言处理从入门到应用》总目录 部分填充的提示模板 提示模板是一个具有.format方法的类,它接受一个键值映射并返回一个字符串(一个提示),以传递给语言模型。与其他方法一样,将提示模板进行…...

论文笔记--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. 文章简介 标题: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-…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

C++实现分布式网络通信框架RPC(2)——rpc发布端
有了上篇文章的项目的基本知识的了解,现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...