【C++】多态的概念/重写/虚表/抽象类

news2024/11/17 13:41:43

多态

  • 多态的概念
  • 多态的定义和实现
  • 重写
  • 抽象类
  • 多态的原理
  • 虚表的构建原理
  • 虚函数的调用原理

多态的概念

多态就是多种形态,传递不同的对象,会调用不同的方法。

多态的定义和实现

那么在C++语法中,多态是如何实现的呢?

我们首先要在继承体系下,要构成多态,需要满足以下两个条件:
1.通过基类的指针或引用来调用虚函数
2.被调用的函数必须是虚函数,并且派生类要对基类的虚函数进行重写。

我们可能又有一个问题了,什么是虚函数?
实际上在类中,被virtual修饰的函数就是虚函数

class A
{
public:
	virtual void fun()
	{
		cout << "virtual func()";
	}
};

重写

由于实现多态需要对基类中的虚函数进行重写,我们就要明白什么是虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。
换句话说,子类中的函数除了实现方式不同,其他都和基类的虚函数完全一样,这样就实现了虚函数的重写。而这样的方式,实际上子类中重写的函数也是虚函数,我们建议为了更加清晰的看到当前子类的这个函数是虚函数,建议在函数返回值前用virtual修饰。(修饰不修饰,它都是虚函数)

而有两种额外情况下,即使重写的虚函数返回值不同或者名称不同,依然构成虚函数的重写:
协变和析构函数
协变:派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

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

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

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;
	delete p2;
	return 0;
}

由于虚函数也是可以继承的,当我们将子类中重写了基类的虚函数也继承下来,也就意味着还能在孙类中继续重写,从而实现多态,那么我们如果不想让孙类再重写子类中的虚函数,就可以给子类中重写的虚函数末尾加上final进行修饰

而当前子类某些函数是否已经重写了基类的虚函数,需要进行判断,就可以使用override进行判断,加到要判断的虚函数末尾,如果报错,则说明没有进行重写

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

抽象类

抽象类的概念:在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。
换句话说:如果存在纯虚函数,那么这个类就是抽象类,只能被继承,不能被实例化,并且只有继承后子类将基类中所有的纯虚函数重写后,子类才可以进行实例化。

多态的原理

一个类中,如果定义了虚函数,那么在对象模型中就存在一个虚表,这个虚表实际上就是一个函数指针数组,数组的每个元素都是虚函数的地址。

注:在同一个类中,不同对象共用同一个虚表,并且这个虚表是在编译阶段生成的。

class A
{
public:
	virtual void fun1(){cout << "A::fun1()" << endl;}
	virtual void fun2(){cout << "A::fun2()" << endl;}
	virtual void fun3(){cout << "A::fun3()" << endl;}
	int _a;
};

在这里插入图片描述
上图就是A类的对象模型,由于fun1,fun2,fun3都是虚函数,则编译器会在编译阶段将三个虚函数的地址放入A类的虚表中,然后给对象模型创建一个虚表指针。

虚表的构建原理

基类虚表的构建原理:在编译阶段,将虚函数按照类中声明顺序依次添加到虚表中。
子类虚表的构建原理:针对单继承
1.将基类虚表中的内容拷贝一份到子类虚表中
2.如果子类重写了基类中某个虚函数,则使用子类的虚函数地址替换虚表中相同位置的基类虚函数地址
3.将子类新增加的虚函数按照在类中声明的先后次序依次添加到虚表的最后。

虚函数的调用原理

对于虚函数的调用原理:
我们首先要在传递参数时,传递基类的引用或指针。
如果代码在运行阶段传递的基类对象,那么调用顺序就是:
1.通过基类对象来获取基类的虚表地址。
2.传递this指针
3.通过传递的this指针来获取虚函数的地址
4.调用对应的虚函数

如果传递的是子类对象,也是类似的调用原理:
1.通过子类对象来获取子类的虚表地址。
2.传递this指针
3.通过传递的this指针来获取虚函数的地址
4.调用对应的虚函数

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

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

相关文章

vue学习 - 基础篇

初始工程结构 这里我们使用script标签从cdn获取vue.js, 而不是使用脚手架vue-cli, 因为cdn比较方便一点, 也不用配置node之类的比较麻烦 index.html <!DOCTYPE html> <html><head><title>VueJS Course</title><link rel"stylesheet"…

第三篇、基于Arduino uno,用oled0.96寸屏幕显示dht11温湿度传感器的温度和湿度信息——结果导向

0、结果 说明&#xff1a;先来看看拍摄的显示结果&#xff0c;如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;本次使用的oled是0.96寸的&#xff0c;别的规格的屏幕不一定适用本教程&#xff0c;一般而言有显示白色、蓝色和蓝黄一起显示的&#xff0…

RabbitMQ日常使用小结

一、使用场景 削峰、解耦、异步。 基于AMQP(高级消息队列协议)协议来统一数据交互,通过channel(网络信道)传递信息。erlang语言开发&#xff0c;并发量12000&#xff0c;支持持久化&#xff0c;稳定性好&#xff0c;集群不支持动态扩展。 RabbitMQ的基本概念 二、组成及工作流…

可见性原子性有序性的+线程传参的方式+Java如何实现多个线程之间共享数据+线程间通信+死锁产生

//为了均衡CPU和内存的速度差异,增加了缓存 导致了可见性的问题; //操作系统增加了进程 线程 分时复用CPU,均衡CPU和io设备的速速差异 导致了原子性问题; //jvm指令重排序(优化指令排序) 导致了有序性的问题 可见性问题是指 线程A修改共享变量,修改后CPU缓存中的数据没有及时同…

Emacs之目前最快补全插件lsp-bridge(八十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

数据分析12——Pandas中数据合并方法

0、前言&#xff1a; 在pandas中进行数据合并的操作和数据库中的join操作非常类似。 1、merge横向合并&#xff1a; 前言&#xff1a;该函数只能做横向合并函数名&#xff1a;merge()函数参数&#xff1a; left: 数据类型为’DataFrame | Series’&#xff0c;需要进行合并的…

[CTF/网络安全] 攻防世界 PHP2 解题详析

[CTF/网络安全] 攻防世界 PHP2 解题详析 index.php.phps扩展名姿势 翻译&#xff1a;你能给这个网站进行身份验证吗&#xff1f; index.php index.php是一个常见的文件名&#xff0c;通常用于Web服务器中的网站根目录下。它是默认的主页文件名&#xff0c;在访问一个网站时&am…

说说计算这事儿:从开关到人工智能

目录 一 前言 二 计算历史 三 计算探秘 四 算力优化 五 未来展望 一 前言 计算本身其实是一个比较抽象的词&#xff0c;或者说比较笼统。很多场景都可能用到计算这个词&#xff0c;因此具体的含义就需要根据上下文来确定。今天我们讨论的计算&#xff0c;是比较狭义的计算…

【环境准备】在虚拟机的Ubuntu下安装VS Code并配置C/C++运行环境

1.点击进入 vscode官网 下载.deb安装包 2.启动虚拟机下的Ubuntu&#xff0c;Windows下的Xftp和Xshell Xftp&#xff1a;用于将刚刚在Windows下下载好的vscode.deb安装包传输到Ununtu中。Xshell&#xff1a;用于远程登录Ununtu&#xff0c;进行 vscode.deb 安装包安装&#xff…

算法26:递归练习

目录 题目1&#xff1a;给你一个字符串&#xff0c;要求打印打印出这个字符串的全部子序列&#xff08;子序列不能重复&#xff09; 题目2&#xff1a;打印一个字符串的全部排列。 题目3&#xff1a;针对题目2&#xff0c;要求去除重复元素 题目4&#xff1a;给定一个字符串…

ARM的读写内存指令与栈的应用

1.基础读写指令 写内存指令&#xff1a;STR MOV R1, #0xFF000000 MOV R2, #0x40000000 STR R1, [R2] 将R1寄存器中的数据写入到R2指向的内存空间 需注意&#xff0c;此命令是将R1中的数据写给R2所指向的内存空间&#xff0c;而不是直接把R1的数据赋给R2&#xff0c;R2寄存器…

chatgpt赋能Python-python3_9如何安装

Python 3.9 安装教程 Python 是一款非常流行的编程语言&#xff0c;而 Python 3.9 是其中的最新版本。不过&#xff0c;有些人可能会遇到一些问题&#xff0c;因为这是一个新版本。在本篇文章中&#xff0c;我们将介绍 Python 3.9 的安装过程&#xff0c;并提供一些关键的步骤…

无线通信网 - 动态主机配置协议 DHCP

文章目录 1 概述2 DHCP2.1 工作原理2.2 报文类型 3 扩展3.1 网工软考真题 1 概述 #mermaid-svg-VTnvU3Vd01Y4gppz {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VTnvU3Vd01Y4gppz .error-icon{fill:#552222;}#merm…

[CTF/网络安全] 攻防世界 Training-WWW-Robots 解题详析

[网络安全] 攻防世界 Training-WWW-Robots 解题详析 在这个小训练挑战中&#xff0c;你将学习 Robots_exclusion_standard&#xff08;机器人排除标准&#xff09;。 robots.txt 文件是由网络爬虫用来检查是否允许他们爬行和索引你的网站或仅部分内容。有时这些文件揭示目录结构…

Vivado HLS 第1讲 软件工程师该怎么了解FPGA架构

Vivado HLS是将基于C/C++描述的算法转化成相应的RTL代码,最终在FPGA上实现。这就要求软件工程师对FPGA的内部架构有一些基本的认识,目的在于保证生成的RTL代码在性能和资源上能够达到很好的平衡。实际上,C语言与FPGA是有一些对应关系的。比如: C语言中的数组可对应于FPGA中…

直方图与直方图均衡化

直方图 图像直方图是用来表现图像中亮度分布的直方图&#xff0c;给出的是图像中某个亮度或者某个范围亮度下共有几个像素&#xff0c;即统计一幅图某个亮度像素数量。 直方图作为一种简单有效的基于统计特性的特征描述子&#xff0c;在计算机视觉领域广泛使用。 它的优点主要…

上下文无关文法、句柄、正规文法、规范推导、文法二义性

目录 上下文无关文法 句柄 正规文法 规范推导 文法二义性 上下文无关文法 上下文无关文法&#xff08;Context-Free Grammar&#xff0c;CFG&#xff09;是一种形式语言&#xff0c;用于描述一类语言的语法结构。它由一组产生式规则组成&#xff0c;每个规则定义了如何将一…

hackthebox htb interface:CVE-2022-28368

本题考察:CVE-2022-28368 CVE-2022-28368 - 通过远程 CSS 字体缓存安装的 RCE 参考: https://www.0le.cn/archives/58.htmlhackthebox-interface信息搜集nmap扫描端口发现开放的22和80PORT STATE SERVICE REASON22/tcp open ssh syn-ac...https://www.0le.cn/archives/58.htm…

Spring 经典面试题总结

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

learn_C_deep_12 (深度理解“取整“、“取余“、“取模“运算、掌握运算符优先级 )

目录 关于“取整” "取整"规则 1、向零取整 2、向-∞取整 3、向∞取整 4、四舍五入 关于"取模和取余" 运算符优先级 关于“取整” #include <stdio.h> int main() {//本质是向0取整int i -2.9;int j 2.9;printf("%d\n", i); /…