多态——C++

news2025/2/25 5:32:38

这里写目录标题

  • 衔接继承总结
  • 继承和组合
    • 白箱复用
    • 黑箱复用
  • 多态的概念
  • 多态的定义以及实现
  • 虚函数重写的两个例外
    • 协变
    • 面试题
    • 析构函数的重写
  • final
  • voerride
  • 重载
  • 隐藏(重定义)
  • 重写(覆盖)
  • 抽象类
    • 什么是抽象类?
  • 实现继承和接口继承
  • 多态的原理
    • 虚函数表
  • 那多态的调用是怎么实现的呢?
  • 动态绑定和静态绑定
    • 切片的区别。
    • 多态的两种形态?
  • 单继承中的虚函数表
    • 虚表存在哪个区域?
  • 多继承中的虚函数表

衔接继承总结

继承的概念和定义,基类和派生类对象赋值转换,这点可以去学学。

一般子类继承级就用公有继承。

继承中的作用域就了解一下就OK。

虚拟继承半了解就行。
我们自己设计尽量不要用菱形继承。但是可以用多继承。

继承和组合

继承是有复用组合是相互独立

优先使用对象组合

白箱复用

在这里插入图片描述
继承这种通过生成派生类的复用称为白箱复用。因为基类的内部细节对子类可见,这样这一定程度破坏了封装。基类的改变,对派生类影响很大。派生类和基类的依赖关系很强,耦合度高。

黑箱复用

黑箱复用对象的内部细节不可见。没有很强的依赖关系,耦合度低
实际中多去使用组合,耦合度低,代码维护性好。

学软件工程一定要画UML图
要学会画类图

C只要不修改公有.就不会影响D。在这里插入图片描述
对象组合是类继承之外的另一种复用的选择。
C对象公有成员D不能直接用。
C对象保护成员D不能直接用。

多态的概念

多态就是:同一个对象去调用同一个函数时会产生不同的效果

多态的关键字是:virtual
在函数面前加上virtual就实现了多态。

构成条件
1.是虚函数+virtual
2.函数名/参数/返回值相同,构成重写,重写也叫作覆盖

注意:要和继承隐藏区分开,隐藏是没有+virtual。

多态有两个要求;
1.子类虚函数重写父类的虚函数。(重写就是三同+virtual)
(三同就是参数,返回值,函数名相同)
2.必须用父类指针或者引用去调用虚函数

为什么要用父类的?不能用子类的?因为只有父类的话,传参的话,符合对象赋值兼容的规则

不能用普通对象调用。

假如用普通对象调用,调用的都是普通对象的,也就是普通人的票
这点为什么不同呢?可以看下面的多态的实现原理。

多态的定义以及实现

怎么让不同的对象调用不同的函数,这里涉及到对象赋值转换,切割

#include <iostream>
using namespace std;
//普通票
class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票——全价" << endl;
	}
};
//学生票 继承普通人
class Student :public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票——半价" << endl;
	}
};
//教师票 继承普通人
class Teacher : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "买票——免费" << endl;
	}
};

void Pay(Person* ptr)
{
	ptr->BuyTicket();
}
int main()
{
	Person p;
	Student s;
	Teacher t;
	Pay(&t);

	return 0; 
}

虚函数重写的两个例外

协变

这个是C++的缺陷。

首先满足协变,需要是对象

Student继承了Person,假如返回值不同,三同中的返回值不同。
子函数需要加virtual,这是好习惯。

class A
{

};
class B :public A
{

};
class Person
{
public:
	virtual A* f()
	{
		cout << "virtual A* Person::f()" << endl;
		return new A;
	}
};
class Student : public Person
{
public:
	virtual B* f()
	{
		cout << "virtual B* Student::f()" << endl;
		return new B;
	}
};

面试题

接口继承。
子类 继承 重写父类虚函数
把virtual继承了下来,缺省参数也继承了下来。

1.接口继承(所以B中func不写virtual也是虚函数,复合多态条件,缺省参数也是用的A::func的1)

2.重写的函数实现。

析构函数的重写

析构函数+virtual构成重写,析构函数的名字统一会被改成destructer

基类的析构函数一般要定义为虚函数,定义为虚函数可以完成析构函数的重写。这样可以不容易导致析构的时候发生错误。

final

final中文叫做最后,可以理解为:最后的类。
1.在函数后面添加final关键字,修饰虚函数,表示该函数不能被重写
2.在基类的后面加final,表示该类不能被继承

voerride

override是写在子类中的,用于检查子类是否完成了重写,如果没有完成重写,就会报错
比如:
1.忘记加了virtual
2.函数仓鼠不同。

重载

1.两个函数在同一作用域
2.函数名/参数不同

隐藏(重定义)

隐藏针对的是两个普通函数
1.两个函数分别在基类和派生类的作用域
2.函数名相同
3.两个基类和派生类的同名函数不构成重写就是重定义(隐藏)。

重写(覆盖)

1.两个函数分别在基类和派生类的作用域
2.函数名/参数/返回值必须相同(协变除外)
3.两个函数必须是虚函数。

抽象类

概念:C++中的包含有纯虚函数的类就叫做抽象类抽象类的不能实例化处对象

子类继承抽象类,必须重写虚函数,才能实例化对象。

什么是抽象类?

抽象的意思就是:在现实中一般没有对应的实体
举个例子:

不能实例化出来对象。比如在学校中,不是具体的实体,而老师学生是具体的实体。

所以就是抽象类

抽象类间接要求子类必须重写,才能实例化对象

实现继承和接口继承

实现继承:普通函数的继承是实现继承。继承的是函数的实现,也就是函数体。

接口继承:虚函数的继承是接口继承,继承的是虚函数的参数/函数名/返回值继承的是接口,目的是为了重写,达成多态

所以不重写不实现多态,就不要虚函数

多态的原理

多态就是实现指针指向哪儿,调用哪的。
怎么实现的呢?

虚函数表

虚函数表里面存放的是虚函数的指针

父类的对象模型是:
1.指向虚函数表的指针
2.成员变量

子类的对象模型:
1.从父类继承下来的指针和变量
2.自己的变量

不同的是子类的虚函数表,在子类的虚函数表中,继承下来的重写的函数不一样

子类中重写的函数覆盖了父类的函数。

这就说明重写:是语法层的概念,对函数实现进行了重写。
原理层的概念:就是子类的虚表,拷贝父类的虚表,然后覆盖重写的那个虚函数

那多态的调用是怎么实现的呢?

多态调用的实现,依靠运行时决议,去指向对象的虚表查看调用函数的地址

普通函数的调用:编译时决议,编译时确定调用函数的地址


指向谁我在谁的虚表里找到调用的。
父类赋值给子类对象,也可以切片,为什么实现不了多态?

为什么对象实现不了多态。只有指针和引用可以实现

原理:编译器很死板,在编译的时候就确定了,编译器在检查时发现不构成多态

怎么才能让对象可以支持多态?对象是不可以的。

动态绑定和静态绑定

1.在程序编译期间确定了程序的行为,
2.构成多态就叫做动态绑定,也叫晚绑定,是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体函数

切片的区别。

对象的切片是把子类中父类的值,拷贝过去,自定义类型是深拷贝。
但是不拷贝虚表指针,因为都指向了同一个虚函数表。导致混乱,不知道调用谁。

对象切片的时候,子类只会拷贝成员给父类对象,不会拷贝虚表指针,假如拷贝就混乱了,父类对象中到底是父类的虚表指针,还是子类的虚表指针?到底该调用谁就混乱了。

但是多态是指向谁调用谁,这不就混乱了,父类指向子类调用的还是子类。所以对象不可能实现多态。

在这里插入图片描述在这里插入图片描述

但是引用和指针切片的话就是直接指向父类或引用父类的那一部分,不存在拷贝的苦恼

多态的两种形态?

静态的多态:函数重载。
为什么有静态的多态,重载实际是在编译期间,根据函数名修饰规则找到不同的函数。

动态的多态:就是上面所讲的。是在运行时候去指向的虚函数表找,而实现的两种形态。

在这里插入图片描述

单继承中的虚函数表

虚函数理论而言要进虚表,但如果只有子类有的虚函数,监视窗口只有父类的虚表,难道子类只有的虚函数不进虚表吗?编译器规定,虚函数都要进虚表的,但是子类一般是拷贝父类的的虚表,然后重写需要重写的虚函数进行覆盖。因为监视窗口一般都是骗人的。但在内存中可以看到。

为了验证这个现象,我们可以写一段程序来打印虚表

虚函数表是一个数组,数组里的每一个元素是函数指针。

这里说明vs的监视窗口看到的虚函数表不一定是真实的,可能会被处理过。

虚表存在哪个区域?

同一个类型的对象,共用一个虚表。
在这里插入图片描述

所以虚表存在的区域只有静态区(数据段)或常量区(代码段)
那么到底在哪个区域呢。
常量区
常量区更加合理,因为静态区一般是全局变量.

我们也可以打印来验证,会发现虚标的地址和常量区的地址最为接近

多继承中的虚函数表

多继承中虚函数表中虚函数的位置

派生类的未重写的虚函数会放在继承的第一个基类部分的虚函数表。

在这里插入图片描述

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

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

相关文章

我为什么选择当程序员

在当今这个数字化时代&#xff0c;程序员已经成为了一个非常受欢迎的职业选择。无论是出于对技术的热爱&#xff0c;还是因为看到了这个行业的广阔前景&#xff0c;越来越多的人选择加入程序员的行列&#xff0c;尤其是最近几年AI带动整体行业的发展。本文将深入探讨人们选择成…

giteegit的连结使用

目标&#xff1a;在windows的本地的git上操作的项目存放到Gitee云端上 不适用于linux的terminal终端下 1.先下载好Git这个软件 2.创建一个文件夹&#xff08;项目名称&#xff09; 然后用gitbash的形式打开 3.创建ssh密钥到Gitee上 因为我们在Git与Gitee上的传输是通过ssh…

用友NC open SQL注入漏洞复现(XVE-2023-29119)

0x01 产品简介 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软件产品系列。这一系列产品基于全球最新的互联网技术、云计算技术和移动应用技术,旨在帮助企业创新管理模式、引领商业变革。 0x02 漏洞概述 用友NC /portal/pt/PaWfm/open接口的proDefPK参数存…

蓝桥杯物联网竞赛_STM32L071KBU6_全部工程及国赛省赛真题及代码

包含stm32L071kbu6全部实验工程、源码、原理图、官方提供参考代码及国、省赛真题及代码 链接&#xff1a;https://pan.baidu.com/s/1pXnsMHE0t4RLCeluFhFpAg?pwdq497 提取码&#xff1a;q497

基于Web的毕业生离校离校管理系统的设计与实现(源码+配套文档)

基于Web的毕业生离校离校管理系统的设计与实现&#xff08;源码配套文档&#xff09; 摘要 越来越多信息化融入到我们生活当中的同时&#xff0c;也在改变着我们的生活和学习方式&#xff0c;当然&#xff0c;变化最明显的除了我们普通民众之外&#xff0c;要数高校学生的生活…

【热门话题】OneFlow深度学习框架介绍

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 OneFlow深度学习框架介绍引言一、OneFlow概述1.1 定位与起源1.2 核心特性数据流…

大屏可视化展示平台解决方案(word原件获取)

1.系统概述 1.1.需求分析 1.2.重难点分析 1.3.重难点解决措施 2.系统架构设计 2.1.系统架构图 2.2.关键技术 2.3.接口及要求 3.系统功能设计 3.1.功能清单列表 3.2.数据源管理 3.3.数据集管理 3.4.视图管理 3.5.仪表盘管理 3.6.移动端设计 3.1.系统权限设计 3.2.数据查询过程设…

ios苹果ipa文件app内测分发有哪些操作流程

哈喽&#xff0c;大家好&#xff0c;咕噜淼淼又来和大家见面啦&#xff0c;在iOS应用开发过程中&#xff0c;进行内测分发是非常重要的一环&#xff0c;它能帮助开发者发现并修复应用中的问题&#xff0c;提升用户体验。上两期咱们一起探讨了一下App内测分发的目的及优势&#…

Linux云计算之Linux基础3——Linux系统基础part-2

1、终端、shell、文件理论 1、终端 终端(terminal)&#xff1a;人和系统交互的必要设备&#xff0c;人机交互最后一个界面&#xff08;包含独立的输入输出设备&#xff09; 物理终端(console)&#xff1a;直接接入本机器的键盘设备和显示器虚拟终端(tty)&#xff1a;通过软件…

20231125 1+X 中级实操考试(id:3777)

//补充完成该类的含参构造方法public Info(String name, int age, String sex, String phone) {this.name name;this.age age;this.sex sex;this.phone phone;} // 请修改该方法&#xff0c;以保证打印对象时输出格式如下&#xff1a;// [namezs;age20;sex男;phone18812349…

错误日志:解决在VScode中调试C++代码断点无效、断点错位的问题

问题可能原因有&#xff1a; 调试时断点无效&#xff0c;大概率是 CMakeLists.txt 设置成了 Release 模式&#xff1b;如果在 CMakeLists.txt 在设置成 Debug 以后&#xff0c;调试时能够停下来&#xff0c;但没在断点处停下&#xff0c;而是停在了别的地方&#xff0c;这就是…

实现iframe里面的页面全屏

首先在父页面引入iframe 监听iframe的方法回调 实现iframe全屏的方法 在iframe页面中&#xff0c;点击全屏后&#xff0c;执行回调函数 就能实现iframe中页面全屏了

你知道 Java 线程池的原理吗?

Java线程池是用于管理和复用线程的机制&#xff0c;它可以帮助开发者有效地管理线程的生命周期和资源&#xff0c;并提高应用程序的性能和稳定性。 1. 线程池概述 在计算机科学中&#xff0c;线程池是一种可用来执行异步任务的线程队列。它主要包含以下几个组成部分&#xff…

Springboot实现链路追踪功能

前言 在日常开发中&#xff0c;一个业务的实现往往会调用很多个方法&#xff0c;当我们去看日志的时候&#xff0c;各种接口的日志打印出来&#xff0c;看着就头疼&#xff0c;压根没办法去定位&#xff0c;而链路追踪就能很好的帮助我们去查看接口从头至尾依次调用了哪些方法…

CSS导读 (元素显示模式)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 三、CSS的元素显示模式 3.1 什么是元素显示模式 3.2 块元素 3.3 行内元素 3.4 行内块元素 3.5 元素…

ssm“健康早知道”微信小程序

采用技术 ssm“健康早知道”微信小程序的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringMVCMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 需求分析 利用ssm、Java、MyEclipse和mysql数据库等知识点&#xff0c;结合相关设…

五一出游 请带上我。必备全家桶。出游变成搬家。千里快递员,这样的人就不要带了。学习过后,你会使用这些句子了吗?

五一出游&#xff0c;即劳动节假期出游&#xff0c;需要准备的物品会根据旅行的目的地、天气状况、交通方式和个人习惯有所不同。以下是一个基本的全家桶必备物品清单&#xff1a; 一、 证件类&#xff1a; 身份证驾驶证&#xff08;如果自驾&#xff09;护照/港澳通行证/台…

C语言学习笔记之操作符篇

目录 算术运算符 移位操作符 整型在内存中的存储&#xff08;补充知识&#xff09; ​编辑左移操作符 右移操作符 位操作符 赋值操作符 复合赋值操作符 单目操作符 关系操作符 逻辑操作符 && 与 || 的计算特点 条件操作符 逗号表达式 下标引用操作符 函…

在linux上面安装xxl-job2.4.0

问题 由于预算有限&#xff0c;用不起lambda去跑定时任务&#xff0c;现在只能在EC2上面自己安装一个单机版的xxl-job了。 步骤 下载压缩包 在这个页面下载压缩包&#xff0c;并本地解压。 https://github.com/xuxueli/xxl-job/releases mysql准备 找到它默认身数据库初始…

Unity(MVC思想)

MVC 一下演示使用MVC和不使用MVC的做法区别。 前两个没有使用MVC 主面板逻辑&#xff1a; mainPanel是该脚本名字 每个场景中不一定存在该面板&#xff0c;单纯的显隐需要去手动挂载过于麻烦。 所以自己读取创建面板出来(每个场景仅创建一次)&#xff0c;存下该面板&#xf…