「C++」虚函数与多态

news2024/12/29 9:25:13
在这里插入图片描述

💻文章目录

  • 📄前言
  • 虚函数
    • 概念
    • 虚函数重写
      • 虚函数的协变
    • 重载、覆盖(重写)、隐藏(重定义)的对比
  • 多态
    • 多态的概念
    • 多态的定义与实现
      • 多态的类型
      • 多态的构成条件
      • 抽象类
    • 多态的原理
      • 虚函数表
    • 多继承中的虚函数表
  • 📓总结


📄前言

面向对象语言中多态是必不可少的一种特性,多态可用于实现代码复用,提高代码的扩展性。

虚函数

概念

虚函数是为了实现多态而实现的功能,被virtual所修饰的成员函数就被称为虚函数。

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

虚函数重写

重写又名覆盖,当派生类又一个更基类完全相同的虚函数,则称子类的虚函数重写了基类的虚函数。重写可以实现使用基类指针/引用指向子类时调用子类的同名虚函数。

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

class B {	//派生类可加virtual也可不加
	virtual void func() { cout << "B::func()" << endl; }
};

虚函数的协变

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

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

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

在这里插入图片描述

多态

多态的概念

多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

举例来说就是,买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

多态的定义与实现

多态的类型

多态又分为动态与静态。

  • 静态多态:在程序编译期间确定了程序的行为,例如函数重载
  • 动态多态:在程序运行期间,根据具体拿到的类型确定程序具体的行为,调用具体的函数。

多态的构成条件

动态多态构成的大前提是必须在继承体系,在继承中构成两个条件:

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

以下的代码是一个简单的多态调用示例:

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

class student {
public:
	virtual void BuyTicket() {
		cout << "买票半价" << endl;
	}
};

void func(person& peo) {
	peo.BuyTicket();
}

抽象类

在虚函数的后面写 = 0,则这个函数就为纯虚函数,包含纯虚函数的类叫做抽象类,抽象类不能实例化对象,派生类必须在重写了纯虚函数后才能实例化出对象。

Car
virtual void Drive()
Benz
virtual void Drive()
BMW
virtual void Drive()
class Car
{
public:
	virtual void Drive() = 0;
};

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

class BMW :public Car
{
public:
 	virtual void Drive()
	{
		cout << "BMW-操控" << endl;
	}
};

void Test()
{
	Car* pBenz = new Benz;
 	pBenz->Drive();
 	Car* pBMW = new BMW;
 	pBMW->Drive();
}

多态的原理

虚函数表

在拥有虚函数的对象里面会存放着一个指向虚函数表的指针,虚表指针。

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
	 	cout << "Base::Func2()" << endl;
	}
private:
	int _val;
};

class Derive : public Base
{
public:
	virtual void Func1()
 	{
 		cout << "Derive::Func1()" << endl;
 	}
};

我们可以看到基类对象的虚表指针全是指向自己的,而派生类对象重写的虚函数覆盖了虚函数表中父类的虚函数所在位置。

在这里插入图片描述

多继承中的虚函数表

我们已经知道单继承中虚函数表的样子,现在来看看多继承中的虚函数表吧。

class Base1 {
public:
	 virtual void func1() {cout << "Base1::func1" << endl;}
	 virtual void func2() {cout << "Base1::func2" << endl;}
private:
 	int b1;
};

class Base2 {
public:
	 virtual void func1() {cout << "Base2::func1" << endl;}
	 virtual void func2() {cout << "Base2::func2" << endl;}
private:
 	int b2;
};

class Derive : public Base1, public Base2 {
public:
	 virtual void func1() {cout << "Derive::func1" << endl;}
	 virtual void func3() {cout << "Derive::func3" << endl;}
private:
 	int d1;
};

typedef void(*VFPTR) ();
typedef long long* pointer;

void PrintVTable(VFPTR vTable[])
{
	// 打印虚表的函数地址并调用它
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
	 	VFPTR f = vTable[i];
		f();
	}
 	cout << endl;
}

Derive d;
VFPTR* vtb1 = (VFPTR*)(*(pointer)&d);
PrintVTable(vtb1);
//强转成父类的指针会使其指向其内存中父类的位置。
PrintVTable((VFPTR*)(*(pointer)(Base2*)&d);

我们可以从结果看出,多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中。
在这里插入图片描述

Base1
virtual void func1()
virtual void func2()
Base2
virtual void func1()
virtual void func2()
Derive
virtual void func1()
virtual void func3()

📓总结

多态调用需要使用基类指针或引用去指向派生类的对象,拥有虚函数的对象都会有一个虚表指针来指向其虚表。

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

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

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

相关文章

数字化转型背景下,企业如何做好知识管理?

在当今数字化转型的时代&#xff0c;企业面临着日益复杂和快速变化的商业环境。知识管理成为了企业成功的关键之一。有效地管理和利用知识资源可以提升企业的创新能力、决策质量和竞争力。以下我列了一些关键的点&#xff0c;讲讲在数字化转型背景下&#xff0c;企业如何可以做…

21款奔驰GLE350升级香氛负离子车载香薰

香氛负离子系统是由香氛系统和负离子发生器组成的一套配置&#xff0c;也可以单独加装香氛系统或者是负离子发生器&#xff0c;香氛的主要作用就是通过香氛外壳吸收原厂的香水再通过空调管输送到内饰中&#xff0c;而负离子的作用就是安装在空气管中通过释放电离子来打击空气中…

浪潮信息云峦服务器操作系统KeyarchOS体验与实践

写在前面 大家好我是网络豆&#xff0c;一名云计算运维人员&#xff0c;本文将会带大家体验一下浪潮信息服务器操作系统云峦KeyarchOS。看看浪潮信息服务器操作系统云峦KeyarchOS的优势与实践操作如何。 背景了解 KeyarchOS是浪潮信息基于Linux Kernel、OpenAnolis等开源技术…

【Python测试开发】:切换窗口和表单

一、多窗口切换 浏览器打开的窗口其实会有一个叫做句柄的概念。 句柄就类似于每一个标签页的ID一样&#xff0c;具有唯一性。 1.1 语法 获取当前窗口句柄&#xff0c;注意后面没有括号哦~ driver.current_window_handle获取所有窗口句柄&#xff0c;结果以列表格式存储&am…

『亚马逊云科技产品测评』活动征文|开发一个手机官网

『亚马逊云科技产品测评』活动征文&#xff5c;开发一个手机官网 授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 前言 …

java系列之 页面打印出 [object Object],[object Object]

我 | 在这里 &#x1f575;️ 读书 | 长沙 ⭐软件工程 ⭐ 本科 &#x1f3e0; 工作 | 广州 ⭐ Java 全栈开发&#xff08;软件工程师&#xff09; &#x1f383; 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲 &#x1f3f7;️ 标签 | 男 自律狂人 目标明确 责任心强 ✈️公…

心梗救治日:我希望他们都能活着到急诊

点击文末“阅读原文”即可参与节目互动 后期 / 朱峰 监制 / 姝琦 运营 / 卷圈&#xff0c;Sand 封面 / 姝琦midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 联合制作 / 美国心脏协会 当意外发生&#xff0c;这期节目可能会让你从手足无措&#xff0c;变成应对有序。 …

Geovia 2022 新功能

​GEOVIA是达索系统3DEXPERIENCE平台旗下品牌产品之一。GEOVIA目前侧重于矿业工程行业&#xff0c;主要用于地理空间大场景建模与仿真模拟&#xff0c;提高整个自然资源部门的可预测性、效率、安全性和可持续性。 新增功能介绍 01.角色更新 达索系统于2021年11月发布了GEOVIA…

国学短剧《我是小影星》栏目火热开拍

近日&#xff0c;国学短剧《我是小影星》栏目花絮拍摄&#xff0c;在上海市徐汇区漕溪路595号A座B1层开拍。该节目招募全国各地的有才华&#xff0c;有表现力怀揣梦想的小朋友来参与节目拍摄。节目旨在以中华传统文化为切入点&#xff0c;通过戏剧、歌舞、音乐等多种艺术形式的…

解析紫光展锐T820 5G芯片——让照片接近原色

紫光展锐系统级安全的高性能5G SoC芯片平台T820&#xff0c;采用八核CPU架构&#xff0c;6nm EUV先进工艺&#xff0c;金融级全内置安全方案&#xff0c;在性能、功耗与5G通信体验等方面&#xff0c;较上一代产品更为出色。 此前&#xff0c;已经为大家讲解过T820的拍照、安全性…

为什么我学了 6 个月 Python,还是找不到工作?

在知乎上有一个特别火的问题&#xff1a; 为什么学了Python&#xff0c;我还是找不到工作&#xff1f; 有人说Python语言不行&#xff0c;有人说中国Python根本就没公司用。在大家群嘲的背后&#xff0c;我们来分析一下&#xff1a; 为什么大家都不看好Python&#xff1f; 学…

hadoop 日志聚集功能配置 hadoop(十一)

由图所示&#xff0c;本文主要是将三台机器log 进行日志聚集查看。图更加直观 1. 首先需要配置历史服务器配置&#xff0c;才可以配置日志聚集功能&#xff1a; hadoop 配置历史服务器 开启历史服务器查看 hadoop (十)-CSDN博客 2. 配置了三台服务器&#xff0c;hadoop22, ha…

uni-app - 弹出框

目录 1.基本介绍 2.原生uinapp 通过uni.showActionSheet实现 3.使用组件 Popup 弹出层 ③效果展示 1.基本介绍 弹出框让我们在需要时在屏幕底部弹出一个菜单&#xff0c;它通常用于在各种应用程序中进行选择操作。Uniapp为我们提供了基本的底部弹出框组件&#xff0c;但它也有…

单线圈无刷直流电机的电机驱动芯片GC1262E/S属于国产芯片可替代APX9262S/茂达

GC1262E/S 是单线圈无刷直流电机的 电机驱动器。 GC1262E/S 具有高效的直接 PWM 控制方式&#xff0c;它可以控制无刷直流电机转 速。它集成了最低速度限制模式、可调速度 斜率控制模式、软启动模式、风扇转速计、 锁保护、自动重启、TSD、OCP 和噪声控制模 式&#xff0c;噪声…

Ubuntu环境下基于libxl库文件使用C++实现对表格的操作

功能 表格不存在则创建后再进行操作创建sheet添加新的工作表在sheet中增加数据设置单元格样式 相关配置 下载地址&#xff1a;libxl选择 LibXL for Linux 4.2.0 i386 x64 armhf aarch64 安装配置 1&#xff0c;使用 tar zxvf 文件名.tar.gz 进行文件解压2&#xff0c;创…

【开源】基于Vue.js的森林火灾预警系统的设计和实现

项目编号&#xff1a; S 019 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S019&#xff0c;文末获取源码。} 项目编号&#xff1a;S019&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 系统基础模块2.3 烟…

【Linux】Linux的常用基本指令

Linux常用基本指令 Linux指令的历史背景前言说明一、 ls 列出文件中的所有内容常用选项 二、pwd 显示当前所在目录进程三、cd 将当前工作目录改变到指定的目录下常用样例 四、touch 1. 更改文档或目录的日期时间 2. 新建一个不存在的文件常用选项 四、mkdir 1. 更改文档或目录的…

队列OJ--循环队列

目录 题目链接&#xff1a;622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09;​​​​​ 题解&#xff1a; ​编辑 代码实现&#xff1a; 完整代码&#xff1a; 题目链接&#xff1a;622. 设计循环队列 - 力扣&#xff08;LeetCode&#xff09;​​​​​ 题解&#x…

html页面直接使用elementui Plus时间线 + vue3

直接上效果图 案例源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"../js/vue3.3.8/vue.global.js"></script><link rel"styles…

全国见!飞桨星河社区五周年,邀你共赴大模型盛宴!

自2018年对外发布以来&#xff0c;飞桨星河社区已汇集660万AI开发者。感谢大家一路见证了飞桨星河社区的成长&#xff0c; 也很荣幸飞桨星河社区陪伴了大家的AI开发旅程。 在这个大模型时代&#xff0c; 飞桨星河社区期待可以帮助开发者们实现自我价值&#xff0c; 获得更多成长…