多态——细致讲解

news2024/9/22 5:40:51

🔶多态基础概念
 🔶概念
  🔱多态性
  🔱多态——重新(覆盖)
 🔶示例
  🔶基本使用方法
  🔶特例
   🔱协变
   🔱析构函数重写
 🔱多态原理
  🔱1. 虚函数形成虚表
  🔱2. 虚函数存储位置(覆盖)
  🔱3. 多态中重写的虚函数存储位置
   🔱1. 重写原理——虚表
   🔱2. 单继承中,子类新增虚函数会存到父类的虚表中——普通继承
   🔱3. 单继承中,子类新增虚函数会存到父类的虚表中——虚继承
   🔱4. 同类公用一个虚表;父类和子类不共用一张虚表
 🔱多态例题
🔱经典问题

多态基础概念

概念

多态性

 1. 静态多态:函数重载和运算符重载
 2. 动态多态:继承和虚函数

多态——重写(覆盖)

 1. 父类的指针/引用调用虚函数
 2. 调用的虚函数必须是子类重写的虚函数
这样就能在指针调用相应的对象函数的时候使用相应的成员函数,具体看示例
这里条件很严格
重写的函数要是一摸一样——返回值,函数名,参数个数,参数位置,参数类型都要完全一样,虚函数之后的const也要一样

示例

基本使用方法
  1. 父类中需要使用virtual修饰函数,子类中virtual可以不写
class A
{
public:
	virtual void func()
	{
		puts("A-->func");
	}
};
class B:public A
{
public:
	virtual void func()
	{
		puts("B-->func");
	}
};
int main()
{
	// 父类指向子类
	A* a1 = new B;
	a1->func();

	// 父类指向父类
	a1 = new A;
	a1->func();

	// 父类引用子类
	B tb;
	A& a2 = tb;
	a2.func();

	// 父类引用父类
	A ta;
	A& a3 = ta; // 不能直接使用a2=ta,引用不能重新赋值,虽然他不会报错,但是他的结果是错的
	a3.func();

	return 0;
}

在这里插入图片描述


  1. final 修饰类——不能继承

在这里插入图片描述

修饰虚函数——不能背重写

在这里插入图片描述

  1. override ——这个函数一定要重新父类的某一个虚函数

在这里插入图片描述

一定要注意这两个关键字加载虚函数结尾

特例
协变

虚函数的返回值可以不一样,只能出现父类返回父类的指针/引用,子类返回子类的指针/引用

在这里插入图片描述

不可以一个返回指针,一个返回引用
只能同时返回指针/同时返回引用

析构函数重写
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}
	~A()
	{
		cout << "delete A" << endl;
	}
	int _a = 1;

};
class B :public A
{
public:
	virtual void fun1()
	{
		cout << "B::fun1" << endl;
	}
	virtual void fun3()
	{
		cout << "B::fun3" << endl;
	}
	~B()
	{
		cout << "delete B" << endl;
	}
	int _b = 2;
};
int main()
{
	A* a = new B;
	delete a;

	return 0;
}

在这里插入图片描述

delete释放看的是类型,也就是说这里delete调用的是A的析构函数
根本上说,delete会被处理成—> destructor() + operator delete,所以他们能构成重写,在具体实现的时候需要写成virtual

	virtual ~A()
	{
		cout << "delete A" << endl;
	}

在这里插入图片描述


多态原理

1. 虚函数形成虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}

};
int main()
{
	A a;
	return 0;
}

在这里插入图片描述

2. 虚函数存储位置

虚函数和普通函数放在一起,虚表存储在代码段

3. 多态中重写的虚函数存储位置
🎭1. 重写原理——虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}

};
class B :public A
{
public:
	virtual void fun1()
	{
		cout << "B::fun1" << endl;
	}
};
int main()
{
	B b;
	return 0;
}

在这里插入图片描述

🎭2. 单继承中,子类新增虚函数会存到父类的虚表中——普通继承
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}

};
class B :public A
{
public:
	virtual void fun1()
	{
		cout << "B::fun1" << endl;
	}
	virtual void fun3()
	{
		cout << "B::fun3" << endl;
	}
};
int main()
{
	B b;
	print((T*)(*(int*)(&b)));

	return 0;
}

在这里插入图片描述
vs中虚表通常在最后一个都是0,Linux不是

在这里插入图片描述

🎭3. 单继承中,子类新增虚函数会存到父类的虚表中——虚继承
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}
	int _a = 1;

};
class B :virtual public A
{
public:
	virtual void fun1()
	{
		cout << "B::fun1" << endl;
	}
	virtual void fun3()
	{
		cout << "B::fun3" << endl;
	}
	int _b = 2;
};
int main()
{
	B b;
	A* a = &b; // 要注意这种写法,确保他能准确跳到下一个虚表处
	print((T*)(*(int*)(a)));

	print((T*)(*(int*)(&b)));

	return 0;
}

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

🎭4. 同类公用一个虚表;父类和子类不共用一张虚表
#include<iostream>
using namespace std;
typedef void (*T)();
class A
{
public:
	virtual void fun1()
	{
		cout << "A::fun1" << endl;
	}
	virtual void fun2()
	{
		cout << "A::fun2" << endl;
	}
	int _a = 1;

};
class B :virtual public A
{
public:
	virtual void fun1()
	{
		cout << "B::fun1" << endl;
	}
	int _b = 2;
};
int main()
{
	A a;

	B b1;
	B b2;
	return 0;
}

在这里插入图片描述


🖼多态例题

class A
{
public:
	virtual void fun(int val = 1)
	{
		cout << "val = " << val << endl;
	}
	virtual void test()
	{
		fun();
	}
};
class B :public A
{
public:
	virtual void fun(int val = 0)
	{
		cout << "val = " << val << endl;
	}
};
int main()
{
	A* a = new B;
	a->test();

	return 0;
}
void print(T a[])
{
	for (int i = 0; a[i] != 0; i++)
	{
		printf("[%d]--->%p\n", i, a[i]);
	}
	puts("");
}
int main()
{
	B b;
	print((T*)(*(int*)(&b)));

	return 0;
}

在这里插入图片描述

  1. 父类指向子类,调用的test函数,test函数是父类的虚函数,类内的函数有一个默认的this指针,test内部调用的fun函数实际上是this->fun(this类型是A*——父类的指针指向虚函数),fun是子类重写的虚函数(函数是子类重写的虚函数)——满足多态条件
  2. 虚函数中的this是根据是否重写确定的,这里的test没有被重写,是A*this指针,然后调用fun,fun是经过重写的函数,所以调用的是重写的函数
  3. 虚函数继承的是函数的接口,重写的是函数的实现

所以缺省值才是1


#include<iostream>
using namespace std;

class A
{
public:
	virtual void fun(int val = 0)
	{
		printf("A::fun()--> %d", val);

	}
	virtual void test()
	{
		fun();
	}
};
class B:public A
{
public:
	void fun(int val = 1)
	{
		printf("B::fun()--> %d", val);
	}
};
int main()
{
	B b;
	b.test();

	return 0;
}

在这里插入图片描述


🔒经典问题

  1. 什么是多态?
  2. 什么是重载、重写(覆盖)、重定义(隐藏)?
  3. 多态的实现原理?
  4. inline函数可以是虚函数吗?

可以,不构成多态就是inline,构成多态就不是inline

  1. 静态成员可以是虚函数吗?

不能,因为静态成员函数没有this指针,使用类型::成员函数
的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。

  1. 构造函数可以是虚函数吗?

不能,虚表在编译时生成
在调用构造函数之后,但是虚表指针在成员初始化之前

  1. 析构函数可以是虚函数吗?

本就应该是,在A* a = new B;这种场景下,在释放子类对象时,需要将析构函数变成虚函数

  1. 对象访问普通函数快还是虚函数更快?

首先如果是普通对象,是一样快的。如果构成多态,就是普通函数快,因为运行时调用虚函数需要到虚函数表中去查找。

  1. 虚函数表是在什么阶段生成的,存在哪的?

虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

  1. C++菱形继承的问题?虚继承的原理?

菱形继承会造成祖宗类数据冗余的问题
在每一个继承自祖宗类的派生类中,使用一个指针指向一个偏移量,根据偏移量找到的地址就是祖宗类的数据,并且这个数据只有一份

  1. 什么是抽象类?抽象类的作用?

抽象类含有形如 virtual void fun() =0; 的基类/派生类
强制派生类重写父类的实现


原理的角度理解,重写之后将fun虚函数进行覆盖test是A*this调用经过重写的虚函数fun符合多态的条件,并且继承的是接口不是实现fun虚函数继承父类函数接口,并使用重写的虚函数实现,最终形成了这个样子


优秀多态文章
优秀多态文章
为什么要使用父类指针和引用实现多态,而不能使用对象?
虚析构函数
虚表位置
虚表位置

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

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

相关文章

链表OJ刷题(二)

制作不易&#xff0c;三连支持一下呗&#xff01;&#xff01;&#xff01; 文章目录 前言一、链表的回文结构二、相交链表三、链表中倒数第k个节点四、环形链表Ⅰ和Ⅱ总结 前言 一、链表的回文结构 链表的回文结构_牛客题霸_牛客网 这里我们需要先了解一下什么叫做回文&#…

测开新手:pytest+requests+allure自动化测试接入Jenkins学习

最近在这整理知识&#xff0c;发现在pytest的知识文档缺少系统性&#xff0c;这里整理一下&#xff0c;方便后续回忆。 在python中&#xff0c;大家比较熟悉的两个框架是unittest和pytest&#xff1a; Unittest是Python标准库中自带的单元测试框架&#xff0c;Unittest有时候…

基于python+django+mysql在线点餐订餐外卖系统设计与实现 开题报告参考

博主介绍&#xff1a;黄菊华老师《Vue.js入门与商城开发实战》《微信小程序商城开发》图书作者&#xff0c;CSDN博客专家&#xff0c;在线教育专家&#xff0c;CSDN钻石讲师&#xff1b;专注大学生毕业设计教育和辅导。 所有项目都配有从入门到精通的基础知识视频课程&#xff…

排序(3)——直接选择排序

目录 直接选择排序 基本思想 整体思路&#xff08;升序&#xff09; 单趟 多趟 代码实现 特性总结 直接选择排序 基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;直到全部待排序的…

Python程序打包成exe可执行文件的常用方法

在Python中,您可以使用一些工具将您的Python程序打包成可执行文件(.exe)。以下是一些常用的工具: PyInstaller: PyInstaller是一个流行的工具,它可以将Python脚本打包成独立的可执行文件,支持Windows、Linux和Mac。您可以使用以下命令安装PyInstaller: pip install pyin…

Matlab:元胞自动机

元胞自动机是一种基于离散空间的动态系统&#xff0c;由许多简单单元按照某些规则进行相互作用和演化而形成的复杂结构。元胞自动机可以用于模拟物理、生物、社会等领域的现象&#xff0c;以及进行优化、图像处理、噪声生成等方面的应用。 例1&#xff1a;生命游戏 nextState…

每日一类:QLabel深入解析

QLabel是Qt中用于显示文本或图像的控件&#xff0c;属于Qt Widgets模块。它是展示静态内容的理想选择&#xff0c;支持富文本格式&#xff0c;使得文本可以包含不同的字体、颜色和链接。QLabel也可以用来显示图像&#xff0c;包括动态图像。此外&#xff0c;它还支持文本和图像…

【硬件相关】IB网/以太网基础介绍及部署实践

文章目录 一、前言1、Infiniband网络1.1、网络类型1.2、网络拓扑1.3、硬件设备1.3.1、网卡1.3.2、连接线缆a、光模块b、线缆 1.3.4、交换机 2、Ethernet网络 二、部署实践&#xff08;以太网&#xff09;1、Intel E810-XXVDA21.1、网卡信息1.2、检查命令1.2、驱动编译 2、Mella…

MySQL进阶:全局锁、表级锁、行级锁总结

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;MySQL进阶&#xff1a;MySQL事务、并发事务问题及隔离级别 &#x1f4da;订阅专栏&#xff1a;MySQL进阶 希望文章对你们有所帮助…

如何根据玩家数量和游戏需求选择最合适的服务器配置?

根据玩家数量和游戏需求选择最合适的服务器配置&#xff0c;首先需要考虑游戏的类型、玩家数量、预计的在线时间以及对内存和CPU性能的需求综合考虑。对于大型多人在线游戏&#xff0c;如MMORPG或MOBA等&#xff0c;由于需要更多的CPU核心数来支持更复杂的游戏逻辑和处理大量数…

【Spring Boot 3】的安全防线:整合 【Spring Security 6】

简介 Spring Security 是 Spring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro&#xff0c;它提供了更丰富的功能&#xff0c;社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity 来做安全框架。小项目有Shiro的比较多&#xff0c;因为相比与Sp…

Python中几个必须知道的函数

Python中自带了几个比较有意思的函数&#xff0c;一般在面试或者笔试基础的时候会问到&#xff0c;其中3个就是map、filter、reduce函数。 1.map(function, iterable) 它第一个要传的元素是函数名或lambda匿名函数表达式&#xff0c;第二个元素传入可迭代对象。 array [1,2,…

【饮食】日常零食 保健食品分类(附食品营养成分表与执行标准,Coursera营养学课程笔记)

程序员生活指南之 【饮食】日常零食 & 保健食品分类和推荐&#xff08;附食品营养成分表与执行标准&#xff09; 文章目录 一、保健食品1、什么是保健食品&#xff1f;2、常见保健食品分类3、常见保健食品推荐 二、日常零食&#xff08;食品营养成分表与执行标准&#xff0…

详解JavaScript的函数

详解 JavaScript 的函数 函数的语法格式 创建函数/函数声明/函数定义 function 函数名(形参列表) { 函数体 return 返回值; // return 语句可省略 } 函数调用 函数名(实参列表) // 不考虑返回值 返回值 函数名(实参列表) // 考虑返回值 示例代码 //定义的没有参数列表&am…

【C语言】sizeof和strlen的比较

1. sizeof和strlen的对比 1.1 sizeof 在学习操作符的时候&#xff0c;我们学习了 sizeof &#xff0c; sizeof 是一个单目操作符&#xff0c; 绝对不是函数&#xff01;&#xff01;&#xff01;sizeof 计算变量所占内存内存空间⼤⼩的&#xff0c;单位是字节。 如果操作数…

three.js 点乘判断平行向量方向异同

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div><div>判断的前提是两个向量平行<el-button click"judge"…

2025张宇考研数学,百度网盘视频课+36讲PDF讲义+真题

张宇老师的课属于幽默生动&#xff0c;会让一个文科生爱上数学&#xff0c;但是有的同学不知道在哪看&#xff0c;可以看一下&#xff1a;2025张宇考研数学全程网盘 docs.qq.com/doc/DTmtOa0Fzc0V3WElI 可以粘贴在浏览器 张宇30讲作为一本基础讲义&#xff1a;和教材…

6、wuzhicms代码审计

wuzhicms代码审计 前言 安装环境配置 服务器要求 Web服务器: apache/nginx/iis PHP环境要求:支持php5.2、php5.3、php5.4、php5.5、php5.6、php7.1 (推荐使用5.4或更高版本!) 数据库要求: Mysql5www/install文件夹即可进入安装页面 审计开始 首页文件index.php&#xff0c…

latex使用Bibtex添加参考文献指南(TeXstudio)

目录 参考链接 Bibtex 使用方法 编译方法 参考链接 https://www.cnblogs.com/whyaza/p/11803493.html &#xff08;Latex&#xff09;Latex TeXstudio Bibtex 使用指南 - 简书 Latex-bibtex使用方法-CSDN博客 Latex插入参考文献的两种方法—自动与手动_latex 参考文献-…

免费下载全网视频系列:一键下载央视视频

之前分享过全网视频下载工具下载视频不求人&#xff0c;免费下载全网视频&#xff0c;今天再分享几个下载央视视频的工具。 第一个是央视频4k下载器&#xff0c;比如下载这个视频https://www.yangshipin.cn/#/video/home?vidv0000313oqb&#xff0c;打开工具在命令行输入 v00…