【C++】C++多态世界:从基础到前沿

【C++】C++多态世界:从基础到前沿

  • 多态的定义及实现
    • 1. 虚函数
    • 2. 虚函数的重写
    • 3. C++11 override 和 final
      • 重载、覆盖(重写)、隐藏(重定义)的对比(关键)
  • 抽象类
    • 1. 概念
    • 2. 接口继承和实现继承
  • 多态的原理
    • 1虚函数表
    • 2. 虚函数存在哪的?虚表存在哪的?
    • 3. 动态绑定与静态绑定
  • 多态的条件:

多态的定义及实现

多态是在不同继承关系类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。
那么在继承中要构成多态还有两个条件:

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

在这里插入图片描述

1. 虚函数

虚函数:即被virtual修饰的类成员函数称为虚函数

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

2. 虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

注意: 重写的是实现,即函数体里面的类容,派生类的函数(包括函数名,参数类型:包括缺省值,返回值)都是直接用的基类的,这也解释了为什么派生类的虚函数可以不加virtual(但是函数名,参数类型,返回值要与基类一样),依然可以构成重写。

在这里插入图片描述

虚函数重写的两个例外:

  1. 协变(基类与派生类虚函数返回值类型不同)
    派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时

  2. 析构函数的重写(基类与派生类析构函数的名字不同)
    如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

3. C++11 override 和 final

  1. final:修饰虚函数,表示该虚函数不能再被重写
class Car
{
public:
 virtual void Drive() final {}//其派生类不可再重写该虚函数
};
class Benz :public Car
{
public:
 virtual void Drive() {cout << "Benz-牛逼" << endl;}
};
  1. override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。
class Car{
public:
 virtual void Drive(){}
};
class Benz :public Car {
public:
 virtual void Drive() override {cout << "Benz-牛逼" << endl;}
};

重载、覆盖(重写)、隐藏(重定义)的对比(关键)

在这里插入图片描述

抽象类

1. 概念

虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类也叫接口类),抽象类不能实例化出对象派生类继承后也不能实例化出派生类对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

2. 接口继承和实现继承

普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。所以如果不实现多态,不要把函数定义成虚函数。

多态的原理

1虚函数表

// 这里常考一道笔试题:sizeof(Base)是多少?vs下答案为8,虚表大小加整形变量b大小
class Base
{
public:
 virtual void Func1()
 {
 cout << "Func1()" << endl;
 }
private:
 int _b = 1;
};

b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些
平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表,。那么派生类中这个表放了些什么呢?

代码举例:

#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;
}

在这里插入图片描述

从监视窗口可得到这些信息:

  1. 基类与派生类的虚表不一样
  2. 基类的虚表中存储的是基类的虚函数Base::Fun1,Base::Fun2
  3. 派生类的虚表中存储的是:(1)在派生类中进行重写了的Derive::Func1,虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。(2)与基类的在派生类中未进行未重写的虚函数Base::Fun2
  4. Func3也继承下来了,但是不是虚函数,所以不会放进虚表。
  5. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr

总结:
派生类的虚表生成:

a.先将基类中的虚表内容拷贝一份到派生类虚表中

b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后

2. 虚函数存在哪的?虚表存在哪的?

  1. 虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中,虚表存的是虚函数指针,不是虚函数
  2. vs :下是存在代码段的
  3. 另外对象中存的不是虚表,存的是虚表指针

3. 动态绑定与静态绑定

  1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态
    比如:函数重载
  2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态

多态的条件:

在这里插入图片描述

  1. 不用派生类(子类)的指针或引用原因:当用子类的指针或引用作为函数形参数时,如果是基类对象作为实参,则无法完成参数的正确传递(子类对象可赋给父类对象,这是由于子类的类容“大于”父类的类容,可通过切片完成,反之一般不可)
  2. 不可用父类对象的原因:当仅仅只是父类对象作为形参,子类对象作为实参,传递过程会发生拷贝

在这里插入图片描述

反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是对象的指针或引用调
用虚函数
。反思一下为什么?

虚函数覆盖:派生类中重写的虚函数会覆盖派生类对象虚表中的基类继承的函数指针,未重写的基类的虚函数依次存储在该派生类对象的虚表中,这样派生类对象的虚表中就同时存在基类与派生类的函数指针,在用基类对象指针或引用调用的时候通过“切片”来达到多态。

对象的指针或引用调用:不会存在拷贝问题。

学习C++必看网站

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

与ai一起作诗(《校园清廉韵》)

与ai对话犹如拷问自己的灵魂&#xff0c;与其说ai助力还不如说在和自己对话。 (笔记模板由python脚本于2024年10月19日 19:18:33创建&#xff0c;本篇笔记适合喜欢python和诗歌的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&…

基于Django的推荐系统、人脸识别登录、微信支付Demo、打卡门禁系统

基于Django的推荐系统、人脸识别登录、微信支付Demo、打卡门禁系统 1、推荐系统 图书管理、电影推荐、音乐推荐、在线课程选修、旅游推荐系统 图书管理点我跳转 电影管理点我跳转 课程管理点我跳转 2、算法 基于用户协同过滤推荐、物品协同过滤推荐、神经网络推荐、随机森…

Linux中如何理解一切皆文件

根据之前的学习我们会有一些少许的疑惑&#xff0c;我们的stdin &#xff0c;stdout&#xff0c;stderr访问的是键盘显示器&#xff0c;然而键盘显示器等他们都有一个共同的特点就是他们都是外设&#xff0c;那么这些外设是怎么被看成是文件的呢&#xff1f; 看图可以知道硬件的…

Jmeter 实战 JDBC配置

​ JDBC JDBC&#xff08;Java Database Connectivity&#xff09;是一种用于执行SQL语句的Java API。通过这个API&#xff0c;可以直接连接并执行SQL脚本&#xff0c;与数据库进行交互。 使用JMeter压力测试时&#xff0c;操作数据库的场景 在使用JMeter进行接口压力测试时…

【去哪儿-注册安全分析报告-缺少轨迹的滑动条】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

什么是分库分表?为什么要分库分表?什么时候需要分库分表?怎么样拆分?(数据库分库分表详解)

文章目录 1、什么是分库分表&#xff1f;1.1、分库分表的概念1.2、分库分表的方式1.2.1、垂直分库1.2.2、垂直分表1.2.3、水平分库1.2.4、水平分表 2、为什么要分库分表&#xff1f;3、什么时候需要分库分表&#xff1f;4、分库分表的数据路由4.1、数据路由的目的4.2、数据路由…

【Linux】磁盘文件系统(inode)、软硬链接

文章目录 1. 认识磁盘1.1 磁盘的物理结构1.2 磁盘的逻辑结构 2. 引入文件系统2.1 EXT系列文件系统的分区结构2.2 inode 3. 软硬链接3.1 软链接3.2 硬链接 在讲过了内存文件系统后&#xff0c;我们可以知道文件分为两种&#xff1a; 打开的文件&#xff08;内存中&#xff09;未…

VMamba:视觉SSM

论文标题&#xff1a;VMamba: Visual State Space Model 论文地址&#xff1a;https://arxiv.org/pdf/2401.10166 摘要 VMamba 是一个视觉骨干网络&#xff0c;基于状态空间模型&#xff08;SSM&#xff09;&#xff0c;其复杂度是线性的。该架构的核心是视觉状态空间&#xff…

uniapp学习(007-2 壁纸项目:详细设计css代码较多)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第70p-第p78的内容 文章目录 客服消息按钮的open-type属性添加客服设置按钮 ifdef和ifndef 实现多端匹配语法实…

【数据结构笔记】优先级队列PriorityQueue

堆序性质&#xff1a;除了根节点&#xff0c;其他节点都不大&#xff08;小&#xff09;于父节点 进而根节点是最大&#xff08;小&#xff09;堆的最大&#xff08;小&#xff09;元 完全二叉堆 物理上是Vector 逻辑上是完全二叉树 层次遍历序列与物理存储顺序相同Rank为…

阅读笔记 Marketing Management Chapter 12

来源: Marketing Management, Kotler and Keller (2016), 15th edition Chapter 12 Addressing Competition and Driving Growth 本章围绕以下问题展开&#xff1a; 为什么公司发展核心业务很重要&#xff1f; 市场领导者如何扩大整个市场并捍卫市场份额&#xff1f; 市场挑…

Go_Parser部署、使用与原理分析

文章目录 前言1、概述2、安装与使用2.1、源码安装2.1.1、部署系统依赖组件2.1.1.1、部署IDA Pro 7.5 SP32.1.1.2、部署Python 3.9.132.1.1.3、部署Go 1.13.1 2.1.2、使用源码安装系统 2.2、使用方法2.2.1、准备测试程序2.2.2、创建IDA Pro项目2.2.3、使用Go_Parser解析二进制程…

【毕业设计】基于SpringBoot的网上商城系统

前言 &#x1f525;本系统可以选作为毕业设计&#xff0c;运用了现在主流的SSM框架&#xff0c;采用Maven来帮助我们管理依赖&#xff0c;所选结构非常合适大学生所学的技术&#xff0c;非常合适作为大学的毕业设计&#xff0c;难以适中。 &#x1f525;采用技术&#xff1a;Sp…

『Mysql集群』Mysql高可用集群之读写分离(二)

前言 主从复制: 解决了Mysql的单点故障问题以及提高MySQL的整体服务性能. 读写分离: 解决的是数据库的读性能问题,分担主库的压力&#xff0c;提高系统的可用性和稳定性。 分库分表: 数据库分表可以解决单表海量数据的查询性能问题&#xff0c;分库可以解决单台数据库的并发…

libaom 编解码项目编码接口文件介绍

对外头文件&#xff1a; 编码端&#xff1a;aom/aom_encoder.h、aom/aomcx.h解码端&#xff1a;aom/aom_decoder.h、aom/aomdx.h aom/aom_encoder.h 该头文件包了aom/aom_codec.h、aom/aom_external_partition.h头文件介绍&#xff1a;当前文件描述了应用程序与视频编码器算法之…

基于tfjs实现线性回归等基本模型

目录 1.回归模型基础概念与应用综述 1.1 线性回归&#xff08;Linear Regression&#xff09; 1.2 多元线性回归&#xff08;Multiple Linear Regression&#xff09; 1.3 广义线性回归&#xff08;Generalized Linear Model, GLM&#xff09; 1.4 逻辑回归&#xff08;Lo…

关于武汉芯景科技有限公司的限流开关芯片XJ6241开发指南(兼容LTC4411)

一、芯片引脚介绍 1.芯片引脚 二、系统结构图 三、功能描述 1.CTL引脚控制VIN和VOUT的通断 2.CTL引脚控制STAT引脚的状态 3.输出电压高于输入电压加上–VRTO的值&#xff0c;芯片处于关断状态

免费开源Odoo软件如何实现电商仓库高效发货

世界排名第一的免费开源ERP软件Odoo&#xff0c;拥有非常强大的仓库管理WMS功能。本文以电商仓库发货管理为例&#xff0c;介绍电商订单的仓库发货作业的各种方法。电商订单仓库发货流程&#xff0c;通常分为三个步骤&#xff0c;即拣货、打包、发货。根据仓库日处理订单数量的…

HTTP Proxy环境下部署Microsoft Entra Connect和Health Agents

在企业环境中&#xff0c;时常需要通过使用HTTP Proxy访问Internet&#xff0c;在使用HTTP Proxy访问Internet的环境中部署Microsoft Entra Connect和Microsoft Entra Connect Health Agents可能会遇到一些额外的配置步骤&#xff0c;以便这些服务能够正常连接到Internet。 一…

linux系统之jar启动脚本

编辑linux启动脚本 执行 vi run_blog 按i 进入编辑&#xff0c;复制以下代码&#xff0c;并根据当前环境修改三个参数。以下是详细完整脚本代码&#xff1a; #!/bin/bash# 配置部分 JAR_PATH"/path/to/your/app.jar" # 替换为你的 JAR 文件的实际路径 L…