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

【C++】学习笔记——多态_1

文章目录

  • 十二、继承
    • 8. 继承和组合
  • 十三、多态
    • 1. 多态的概念
    • 2. 多态的定义和实现
      • 虚函数重写的两个特殊情况
      • override 和 final
    • 3. 多态的原理
      • 1. 虚函数表
  • 未完待续


十二、继承

8. 继承和组合

我们已经知道了什么是继承,那组合又是什么?下面这种情况就是 组合

class A
{//
};class B
{
private:A _a;
};

组合和继承都是让代码复用,但是继承的复用是一种 白箱复用 ,父类的内部细节是对子类透明的,根透明箱子一样。而组合的复用是一种 黑箱复用 ,因为对象的内部细节是不可见的。
继承一定程度破坏了父类的封装,父类的改变,对子类有很大的影响。子类和父类间的依赖关系很强,耦合度高组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于保持每个类被封装

优先使用对象组合,而不是继承。

public继承是一种 is-a 的关系。也就是说每个子类对象都是一个父类对象。
组合是一种 has-a 的关系。假设B组合了A,每个B对象中都有一个A对象。

十三、多态

1. 多态的概念

多态 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成某个行为时会产生出不同的状态 。举个栗子:比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

2. 多态的定义和实现

我们先实现一下多态,来尝尝鲜:

#include<iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "买票-全价" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "买票-半价" << endl;}
};// 多态
void Func(Person& p)
{p.BuyTicket();
}int main()
{Person ps;Student st;Func(ps);// 子类可以赋值给父类---切片Func(st);return 0;
}

在这里插入图片描述
在继承中想要构成多态是有条件的。

1. 必须通过父类的指针或者引用调用虚函数。
2. 被调用的函数必须是 虚函数 ,且子类必须对父类的虚函数进行重写。

虚函数的重写(覆盖/隐藏):子类中有一个跟父类完全相同的虚函数(即子类虚函数与父类虚函数的 返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了父类的虚函数。(实际上父类的虚函数可以被子类继承,所以只要父类写上 virtual ,子类即使不写 virtual 也能构成重写)

关于重写:重写是重写的 实现仅仅会改变实现方式,声明并不会改变

虚函数重写的两个特殊情况

协变
在虚函数重写时,父类和子类的虚函数返回类型可以不同,但要求返回类型必须是父子类关系的指针和引用,则称为 协变

#include<iostream>
using namespace std;class A {};
class B : public A {};class Person
{
public:// 虚函数重写,返回类型是对应的指针或引用virtual A* f(){cout << "A::f()" << endl;return new A;}
};class Student : public Person
{
public:// 虚函数重写,返回类型是对应的指针或引用virtual B* f(){cout << "B::f()" << endl;return new B;}
};int main()
{Person* p = new Student;p->f();return 0;
}

在这里插入图片描述
当返回类型是对应的指针或引用时成功实现多态,当返回类型不是时:

#include<iostream>
using namespace std;class A {};
class B : public A {};class Person
{
public:// 返回类型不同且不说相应的指针或引用virtual A f(){cout << "A::f()" << endl;return *new A;}
};class Student : public Person
{
public:// 返回类型不同且不说相应的指针或引用virtual B f(){cout << "B::f()" << endl;return *new B;}
};int main()
{Person* p = new Student;p->f();return 0;
}

在这里插入图片描述
析构函数的重写
如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加 virtual 关键字,都与父类的析构函数构成重写。原因是编译器对析构函数的名称做了特殊处理,编译后所以析构函数的名称统一处理成 destructor

当父类的析构函数不是虚函数时,如下情况则会:

#include<iostream>
using namespace std;class Person
{
public:~Person(){cout << "~Person()" << endl;}
};class Student : public Person
{
public:~Student(){cout << "~Student()" << endl;}
};int main()
{// 父类指针指向父类对象Person* p1 = new Person;// 父类指针指向子类对象Person* p2 = new Student;delete p1;cout << endl;delete p2;return 0;
}

在这里插入图片描述
没能成功进行多态调用,访问的还是父类的析构函数。当父类的析构函数是虚函数时:

#include<iostream>
using namespace std;class Person
{
public:virtual ~Person(){cout << "~Person()" << endl;}
};class Student : public Person
{
public:// 子类可以不写 virtual ,自动构成虚函数重写~Student(){cout << "~Student()" << endl;}
};
// 只有派生类Student的析构函数重写了Person的析构函数
//下面的delete对象调用析构函数,才能构成多态
//才能保证p1和p2指向的对象正确的调用析构函数
int main()
{// 父类指针指向父类对象Person* p1 = new Person;// 父类指针指向子类对象Person* p2 = new Student;delete p1;cout << endl;delete p2;return 0;
}

在这里插入图片描述
成功构成多态调用。我们怎么分辨 普通调用多态调用 呢?

普通调用 看指针或引用或者对象的类型
多态调用 看指针或引用指向的对象

在这里插入图片描述

override 和 final

如果我们想实现一个类,使其不能被继承,应该怎么做?方法一:将父类的构造函数私有化,由于子类的构造函数必须调用父类的构造函数,所以父类的构造函数私有化会导致子类无法实例出对象。方法二:使用关键字 final

// 父类增加关键词 final
class A final
{//
};class B : public A
{//
};

在这里插入图片描述

final 还可以修饰虚函数,表示该虚函数不能再被重写。

class Car
{
public:virtual void Drive() final{//}
};class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒适" << endl;}
};

在这里插入图片描述
override 可以检查子类虚函数是否重写了父类某个虚函数,如果没有重写则编译报错。

class Car
{
public:void Drive(){//}
};class Benz :public Car
{
public:// override 写在子类后面virtual void Drive() override{cout << "Benz-舒适" << endl;}
};

在这里插入图片描述

3. 多态的原理

1. 虚函数表

这里常考一道笔试题:sizeof(Base)是多少?

class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};int main()
{Base bb;cout << sizeof(Base) << endl;return 0;
}

在这里插入图片描述
答案是:8;原因是,int 占 4 个字节,而只要类里面有虚函数,类就会在内部 额外生成一个指针 ,指针指向函数指针数组,函数指针数组里存的都是虚函数的地址,称为 虚函数表 。指针占 4 个字节,故答案是 8 。
在这里插入图片描述
对于上面的代码,我们再进行改造一下:

#include<iostream>
using namespace std;class Base
{
public:// 虚函数virtual void Func1(){cout << "Base::Func1()" << endl;}// 虚函数virtual void Func2(){cout << "Base::Func2()" << endl;}// 普通函数void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};class Derive : public Base
{
public:// 虚函数重写virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};int main()
{Base b;Derive d;return 0;
}

在这里插入图片描述
我们发现,父类b对象和子类d对象虚函数表是不一样的,这里我们发现Func1完成了重写,所以d的虚函数表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚函数表中虚函数的覆盖。b对象的虚函数表先拷贝一份父类的虚函数表,然后子类重写的函数覆盖进b对象的虚函数表。重写是语法的叫法,覆盖是原理层的叫法。Func3由于不是虚函数,所以没有进入虚函数表。
运行时是通过本身的父类虚函数表或者切片的父类虚函数表(自己的)找到相应的虚函数,不同的对象虚函数表不同,因此实现多态。


未完待续

相关文章:

【C++】学习笔记——多态_1

文章目录 十二、继承8. 继承和组合 十三、多态1. 多态的概念2. 多态的定义和实现虚函数重写的两个特殊情况override 和 final 3. 多态的原理1. 虚函数表 未完待续 十二、继承 8. 继承和组合 我们已经知道了什么是继承&#xff0c;那组合又是什么&#xff1f;下面这种情况就是…...

C++map容器关联式容器

Cmap 1. 关联式容器 vector、list、deque、forward_list(C11)等STL容器&#xff0c;其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身&#xff0c;这样的容器被统称为序列式容器。而map、set是一种关联式容器&#xff0c;关联式容器也是用来存储数据的&#xff0…...

TS-抽象类和静态成员

目录 1&#xff0c;抽象类1&#xff0c;为什么需要抽象类2&#xff0c;抽象成员3&#xff0c;设计模式-模板模式 2&#xff0c;静态成员1&#xff0c;什么是静态成员2&#xff0c;设计模式-单例模式 1&#xff0c;抽象类 1&#xff0c;为什么需要抽象类 有时&#xff0c;某个…...

SharePoint 使用renderListDataAsStream方法查询list超过5000时的数据

问题&#xff1a; 当SharePoint List里的数据超过5000时&#xff0c;如果使用常用的rest api去获取数据&#xff0c;例如 await this.sp.web.lists.getByTitle(Document Library).rootFolder.files.select(*, listItemAllFields).expand(listItemAllFields).filter(listItemA…...

2024042001-计算机网络 - 物理层

计算机网络 - 物理层 计算机网络 - 物理层 通信方式带通调制 通信方式 根据信息在传输线上的传送方向&#xff0c;分为以下三种通信方式&#xff1a; 单工通信&#xff1a;单向传输半双工通信&#xff1a;双向交替传输全双工通信&#xff1a;双向同时传输 带通调制 模拟信号…...

通过java将数据导出为PDF,包扣合并单元格操作

最近项目中需要将查询出来的表格数据以PDF形式导出&#xff0c;并且表格的形式包含横向行与纵向列的单元格合并操作&#xff0c;导出的最终效果如图所示&#xff1a; 首先引入操作依赖 <!--导出pdf所需包--><dependency><groupId>com.itextpdf</groupId&…...

Java内存模式以及volatile关键字的使用

1.Java内存模型 &#xff08;1&#xff09;Java 内存模型&#xff08;Java Memory Model&#xff0c;简称 JMM&#xff09;&#xff0c;它是一个抽象的概念&#xff0c;JMM是和多线程相关的&#xff0c;它是一组规范&#xff0c;描述了一组规则&#xff0c;定义了多线程对共享…...

每日5题Day3 - LeetCode 11 - 15

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int maxArea(int[] height) {//这道题比较特殊&#xff0c;因为两边是任意…...

路由器、交换机和网卡

大家使用VMware安装镜像之后&#xff0c;是不是都会考虑虚拟机的镜像系统怎么连上网的&#xff0c;它的连接方式是什么&#xff0c;它ip是什么&#xff1f; 路由器、交换机和网卡 1.路由器 一般有几个功能&#xff0c;第一个是网关、第二个是扩展有线网络端口、第三个是WiFi功…...

腾讯开源混元DiT文生图模型,消费级单卡可推理

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。 总结链接…...

shell脚本基础(if/else结构)

命令是双向选择语句&#xff0c;当用户执行脚本时如果不满足if后的表达式也会执行else后的命令&#xff0c;所以有很好的交互性。其结构为&#xff1a; if expression1 then command … command else command … command fi vim ifelse_exam.sh #ifelse_exam.sh #!/bin/bashec…...

万字长文破解 AI 图片生成算法-Stable diffusion (第一篇)

想象一下&#xff1a;你闭上眼睛&#xff0c;脑海中构思一个场景&#xff0c;用简短的语言描述出来&#xff0c;然后“啪”的一声&#xff0c;一张栩栩如生的图片就出现在你眼前。这不再是科幻小说里才有的情节&#xff0c;而是Stable Diffusion——一种前沿的AI图片生成算法—…...

Linux---编辑器vim的认识与简单配置

前言 我们在自己的电脑上所用的编译软件&#xff0c;就拿vs2022来说&#xff0c;我们可以在上面写C/C语言、python、甚至java也可以在上面进行编译&#xff0c;这种既可以用来编辑、运行编译&#xff0c;又可以支持很多种语言的编译器是一种集成式开发环境&#xff0c;集众多于…...

lucene中Collector类、CollectorManager类区分和用法

我的lucene版本是9.10.0&#xff0c;请说明Collector类、CollectorManager类区分和用法&#xff0c;尽量详细点 在 Lucene 9.10.0 中&#xff0c;Collector 类和 CollectorManager 类都是用于搜索结果的收集和处理 Collector 类 Collector 类是一个接口&#xff0c;用于收集…...

Android之给Button上添加按压效果

一、配置stateListAnimator参数实现按压效果 1、按钮控件 <Buttonandroid:id"id/mBtnLogin"android:layout_width"match_parent"android:layout_height"48dp"android:background"drawable/shape_jfrb_login_button"android:state…...

python EEL + vue3.js 项目中如何把组件中的函数提升为全局函数

eel官方示例中暴露的js函数是全局函数&#xff0c;vue中的自定义函数作用域通常都是组件范围内。要让eel.js调用&#xff0c;需要将其升为全局可用。 一般方法有 app.config.globalProperties 或 mixin等。 main.js //main.jsimport { createApp } from vue import App from…...

sqli-labs靶场第十四关

目录 1&#xff1a;分析 找闭合符&#xff1a; 2&#xff1a;开始注入 报错注入&#xff1a; 注入数据库名&#xff1a; 注入表名&#xff1a; 注入列名&#xff1a; 注入具体值&#xff1a; 1&#xff1a;分析 经过我们的实验发现当我们输入的密码后面存在双引号时会报…...

【C语言】6.C语言VS实用调试技巧(1)

文章目录 1.什么是 bug2.什么是调试&#xff08;debug&#xff09;&#xff1f;3.Debug 和 Release4.VS调试快捷键4.1 环境准备4.2 调试快捷键 5.监视和内存观察5.1 监视5.2 内存 1.什么是 bug bug现在一般是指在电脑系统或程序中&#xff0c;隐藏着的一些未被发现的缺陷或问题…...

AIGC行业现在适合进入吗

人工智能、物联网、基因编辑和量子计算等新兴技术领域正在以前所未有的速度发展&#xff0c;这些技术的结合正在重塑我们的世界。在这个充满机遇和挑战的时代&#xff0c;AIGC&#xff08;人工智能、基因编辑和量子计算&#xff09;行业备受关注&#xff0c;许多人都在考虑是否…...

ubuntu CUDA 驱动更新,版本更新,多CUDA版本管理

1 新版本驱动下载 前面介绍过window CUDA驱动更新&#xff0c;但是对于ubuntu 的驱动更新&#xff0c;没有一键操作。 本人笔记本电脑n年前装的CUDA DRIVER仅支持到cuda10.2&#xff0c;实在无法满足这日新月异的科技更新。 左 旧的驱动版本 右 新下载的硬件支持的驱动版本&…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统

Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

LUA+Reids实现库存秒杀预扣减 记录流水 以及自己的思考

目录 lua脚本 记录流水 记录流水的作用 流水什么时候删除 我们在做库存扣减的时候&#xff0c;显示基于Lua脚本和Redis实现的预扣减 这样可以在秒杀扣减的时候保证操作的原子性和高效性 lua脚本 // ... 已有代码 ...Overridepublic InventoryResponse decrease(Inventor…...

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.

这个警告表明您在使用Vue的esm-bundler构建版本时&#xff0c;未明确定义编译时特性标志。以下是详细解释和解决方案&#xff1a; ‌问题原因‌&#xff1a; 该标志是Vue 3.4引入的编译时特性标志&#xff0c;用于控制生产环境下SSR水合不匹配错误的详细报告1使用esm-bundler…...