【c++ 之 多态】

news2024/11/18 18:27:57

目录:

  • 前言
  • 多态
    • 认识多态
    • 多态的定义与实现
      • 构成多态的条件
      • 虚函数
        • 1.协变(基类与派生类虚函数返回值不同)
        • 2.析构函数的重写
        • c++11.两个虚函数修饰关键字:final & override
    • 重载、重写、重定义再理解
  • 抽象类
    • 抽象类的概念
    • 接口继承与实现继承
  • 多态的原理
    • 虚函数表
    • 打印虚函数表
    • 原理剖析
  • 经典例题
  • 总结

前言

打怪升级:第61天
在这里插入图片描述

多态

认识多态

所谓多态,通俗来讲就是多种形态,也就是当一件事情由不同的人去完成会表现出不同的形态,例如买车票:成人全价,学生半价,军人优先等;
再例如测量体重,不同的人去测量,体重仪的表现也会不同。

多态的定义与实现

构成多态的条件

下面我们先来“见一见猪跑”:

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人,全价" << endl;
	}
};

class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生,半价" << endl;
	}
};

void Buy(Person& p)
{
	p.BuyTicket();
}

void Test_p2()
{
	Person p1;
	Student t1;
	Buy(p1);
	Buy(t1);
}

在这里插入图片描述

虚函数

  • 虚函数的定义
    虚函数就是使用 virtual 修饰的成员函数
class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人,全价" << endl;
	}
};
  • 虚函数的重写
    **重写(覆盖)**的条件:
    在子类中存在与父类完全相同的虚函数(三同:函数名、参数、返回值都必须相同),我们称为子类对父类的虚函数进行了重写。
class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "成人,全价" << endl;
	}
};

class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "学生,半价" << endl;
	}
};

这里是引用

上面,我们说的十分肯定 – 必须由三同才可构成重写,
然而,其实是有两个特例存在的 – 1.子类的重写返回值在特殊情况下可以不同;2.析构函数的重写

1.协变(基类与派生类虚函数返回值不同)

当子类和父类的虚函数返回值为有父子关系的类对象时,返回值也可以不同。

class A
{

};

class B :public A
{

};

class Person
{
public:
	virtual Person& BuyTicket()
	{
		cout << "成人,全价" << endl;
		return *this;
	}
};

class Student : public Person
{
public:
	virtual Student& BuyTicket()
	{
		cout << "学生,半价" << endl;
		return *this;
	}
};

在这里插入图片描述

可以让父类虚函数返回子类引用,子类虚函数返回父类引用吗?
不可,虚函数的重写实际上是对 从父类继承下来的虚函数的实现进行重写,声明部分是完全继承的,因此,
如果父类虚函数返回子类引用,就会使得子类中的虚函数使用父类对象初始化子类对象,(我们可以使用子类对象初始化父类对象 – 会进行切片,但是父类中不一定拥有子类的全部成员,无法完成对子类的初识化)。

2.析构函数的重写

如果基类的析构函数是虚函数,此时派生类的析构函数无论是否加 virtual,都与基类的析构函数构成重写。
这里虽然基类与派生类的析构函数函数名不同,看起来好像违反了 三同 的规则,
其实不然,这里是编译器在底层做了特殊处理:编译之后所有析构函数的名称都会被处理为destruction

这里是引用在这里插入图片描述

c++11.两个虚函数修饰关键字:final & override

final修饰父类虚函数:该虚函数不可再在子类中进行重写了。
override修饰子类虚函数:该虚函数必须是父类的虚函数的重写。

在这里插入图片描述

重载、重写、重定义再理解

也就是说:两个基类和派生类中的同名函数 不构成重写 就是重定义。


抽象类

抽象类的概念

虚函数后面写上 = 0 ,这个虚函数就变成了纯虚函数, 包含纯虚函数的类称为抽象类(也叫接口类),包含纯虚函数的类无法实例化出对象,抽象类的派生类想要实例化出对象必须对纯虚函数实现重写,否则派生类也是抽象类。

接口继承与实现继承

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

这里是引用


多态的原理

多态,如果只看表面应用 – 不同的对象调用"同一个函数"表现也不一样,看起来感觉好像很神奇、很厉害,居然可以“进行判断?”,
那么到底是不是这样呢,让我们去底层一探究竟吧~。
(注:以下数据测试环境为 vs2022,x86)

虚函数表

class Base
{
public:
	virtual void Print()
	{
		cout << "Base::Print" << endl;
	}
	int _bval;
};


void Test_p3()
{
	Base b1;
	cout << sizeof(b1) << endl;

}

我们来计算一下Base类的大小:
按照我们以前的知识:成员函数是放在代码段,对象中只有普通成员变量, 因此,Base的大小应该是4;

这里是引用

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

class Base
{
public:
	virtual void Print1() {}

	virtual void Print2() {}

	int _bval = 1;
};

class Derive :public Base
{
public:
	virtual void Print1() {}

	virtual void Print3() {}

	int _dval = 10;
};

void Test_p4()
{
	Base b1;

	Derive d1;
}

这里是引用

这里有一点我们需要注意:虚函数表存在哪里?虚函数又存在哪里?
虚函数表存在对象中,虚函数存在虚函数表中,吗?
不是的,对象中存的是一个虚函数表指针,虚函数表中存的也只是虚函数的指针,
至于虚函数表和虚函数,其实都存在于内存中的代码段

打印虚函数表

typedef void(*VFPTR)();  //  定义 VFPTR为  void(*)() -- 函数指针类型

void VFTable(VFPTR*table)
{
	/*while (*table)
	{
		(*table)();
		++table;
	}*/
	for (int i = 0; table[i]; ++i)
	{
		printf("[%d]->", i);
		table[i](); // 函数调用
	}
	cout << endl;
}

void Test_p4()
{
	Base b1;
	Derive d1;

	//  要打印虚函数表,我就要先获取虚函数表的地址 -- 通过上面几次的查看我们可以看到 -- 虚函数表地址存放在对象的最前面
	VFTable((VFPTR*)(*(int*)&b1));
	VFTable((VFPTR*)(*(int*)&d1));

	VFTable(*(VFPTR**)&b1); 
	VFTable(*(VFPTR**)&d1);
}

这里是引用在这里插入图片描述


原理剖析

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

补充:

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

经典例题

  • 1
class Base1 { public: int _b1; };
class Base2 { public: int _b2; };
class Derive : public Base1, public Base2 { public: int _d; };

int main() 
{
	Derive d;
	Base1* p1 = &d;
	Base2* p2 = &d;
	Derive* p3 = &d;
	return 0;
}

在这里插入图片描述

  • 2
class AA
{
public:
	virtual void Print(int a = 1)
	{
		cout << "a = " << a << endl;
	}

	virtual void Call() { Print(); }
};

class BB :public AA
{
public:
	virtual void Print(int b = 0) 
	{ 
		cout << "b = " << b << endl; 
	}
};

void Test_p1()
{
	BB p;
	p.Call();
}

在这里插入图片描述

总结

多态的重点

  1. 就是要了解多态构成的条件:父类的指针或引用;虚函数重写。
  2. 就是知道了解虚函数表的原理:存的是虚函数地址。
  3. 清楚多态实现的原理。


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

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

相关文章

强大的JSON格式化和编辑工具zjson

本文软件应网友 小超 的需求而制作&#xff0c;软件本身已经 2年未更新&#xff0c;请知悉~ 什么是 zjson ? 转杰森&#xff08;zjson&#xff09; 是一个强大的 JSON 格式化和编辑工具&#xff0c;支持在线版和 Electron应用安装&#xff0c;使用 MEAN-STACK ( MongoDB Expr…

【ArcGIS】常见问题总结

1 arcgis如何打开*.adf文件 在处理数据时发现&#xff0c;获取到的土地利用类型数据有两个文件夹&#xff0c;一个叫info&#xff0c;另一个叫lucc2010&#xff08;年份&#xff09;&#xff0c;打开lucc2010里面是一系列的*.adf文件&#xff0c;数据应该如何打开呢&#xff1…

Red Hat Enterprise Linux 9的简介

1.3 Red Hat Enterprise Linux 9的简介 2022年5月&#xff0c;红帽公司&#xff08;Red Hat&#xff09;发布了Red Hat Enterprise Linux 9.0&#xff08;简称RHEL 9.0&#xff09;正式版。Red Hat Enterprise Linux是全球领先的企业级Linux操作系统&#xff0c;已获得数百个…

重大剧透:你不用ChatGPT,它砸你饭碗

早晨看到路透社报道&#xff0c;盖茨说&#xff0c;与其争论技术的未来&#xff0c;不如专注于如何更好地利用人工智能。 这可能是他对马斯克他们呼吁暂停AI研发6个月的一种回应吧。 有种古语说&#xff1a;天下大势&#xff0c;浩浩汤汤&#xff0c;顺之者昌&#xff0c;逆之者…

ai模型训练生成效果 chilloutmix_NiPrunedFp32Fix.safetensors

模型名称&#xff1a; chilloutmix_NiPrunedFp32Fix.safetensors 关键词 extremely detailed CG unity 8k wallpaper,(masterpiece),(best quality),(ultra detailed),(ultra realistic),(Best character details:1.2),dynamic angle,professional lighting, photon mapping, …

【4. ROS的主要通讯方式:Topic话题与Message消息】

【4. ROS的主要通讯方式&#xff1a;Topic话题与Message消息】 1. 前言1.1 王者解释结点通讯&#xff1a;1.2 通讯小结 2. 灵活的Topic话题图解2.1 话题注意细节2.2 外延补充 3. Message消息图解3.1 消息类型3.2 查看标准消息类型std_msgs 4. 使用C实现Publisher发布者4.1 发布…

自动化工具 WEB 自动化工具

背景 使用自动化测试框架编写用例的时候&#xff0c;维护元素信息以及脚本较为麻烦。对应新手来说&#xff0c;编写脚本的能力有限&#xff0c;使用工具会更容易入手。最重要的是可视化操作让我觉得体验舒服。 演示地址 地址&#xff1a;hippo 账号&#xff1a;test 密码&am…

探索文件世界:用Python创建交互式文件浏览器

目录 引子&#xff1a; 应用场景&#xff1a; 源代码&#xff1a; 源代码说明&#xff1a; 效果如下所示: D:\spiderdocs\previewjpegmdfile.py 引子&#xff1a; 在许多应用程序中&#xff0c;需要方便地查看特定文件夹中的所有图片&#xff0c;例如图库管理器、相册应用…

泛型(一)

泛型&#xff1a;标签&#xff08;类型参数&#xff09; 所谓泛型&#xff0c;就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时&#xff08;例如&#xff0c;继承或实现这个接口&#xff0c;用这个类型…

Python 数据存储 ---->方式

我的个人博客主页&#xff1a;如果’真能转义1️⃣说1️⃣的博客主页 关于Python基本语法学习---->可以参考我的这篇博客&#xff1a;《我在VScode学Python》 数据存储是指在数据加工处理过程中将产生的临时文件或加工结果以某种格式保存。 常用的数据存储格式包括 TXT、Exc…

Java线程池常见面试题详解

线程池 池化技术 池化技术是一种常见的编程技巧, 把一些能够复用的东西&#xff08;比如说数据库连接、线程&#xff09;放到池中&#xff0c;避免重复创建、销毁的开销&#xff0c;在需要时可以重复使用这些预先准备的资源&#xff0c;从而极大提高性能。(提前保存大量资源,…

人生路上就是要不断打破认知边界

2023-4-25分享新知1.多角度看问题&#xff0c;竭尽全力才能找到突破口。结合客户需求解决问题2.新产品&有用的产品对用户提供有价值的产品3.全力就是所有部门都参与开发新产品&#xff0c;好机会就是有时间开发新产品&#xff0c;好产品就是一家企业的底气&#xff0c;产品…

SpringBoot 2.7.X 一套代码适配多种数据库讲解(图文详细)

文章目录 SpringBoot 2.7.X 一套代码适配多种数据库讲解(图文详细)1 简介1.1 概述1.2 环境安装1.3 测试脚本 2 基于Mybatis 方式2.1 添加DatabaseIdProvider配置2.2 在Mybatis的XML中,指定SQL语句的databaseId标签2.3 控制器接口样例2.4 呈现效果 3 基于MP框架Wrapps条件构造器…

AutoGPT也有Web UI了

AutoGPT能够在你的电脑上做任何你想做的事情&#xff0c;并且我们在前面的文章中也介绍了其他的一些类似的应用。 但是AutoGPT最大的一个问题是只能通过命令行界面(CLI)运行&#xff0c;这样就算是专业的技术人员使用起来也很麻烦&#xff0c;想想Stable Diffusion&#xff0c…

关于jeecgboot中遇到的问题及解决方案

1&#xff0c;关于数据权限问题 目的&#xff1a;一个人对应多个部门&#xff0c;部门下可能有子部门&#xff0c;过滤数据权限 解决方案&#xff1a; 方案①&#xff08;不推荐&#xff09;&#xff1a;如果后台是手写的sql&#xff08;没有用到mybatis-plus&#xff09;&…

一文弄懂标识符的规则和instanceof关键字

“世间有吸引法则&#xff0c;在于你有没有价值&#xff0c;价值来于物质资本和精神资本” 标识符的命名规则 标识符的含义&#xff1a;是指在程序中&#xff0c;我们自己定义的内容&#xff0c;例如方法名&#xff0c;变量名或者类名 命名规则&#xff1a;&#xff08;硬性要…

AlgoC++第六课:BP反向传播算法

目录 BP反向传播算法前言1. MNIST2. 感知机2.1 前言2.2 感知机-矩阵表示2.3 感知机-矩阵表示-多个样本2.4 感知机-增加偏置2.5 感知机-多个输出2.6 总结2.7 关于广播 3. BP4. 动量SGD5. BP示例代码总结 BP反向传播算法 前言 手写AI推出的全新面向AI算法的C课程 Algo C&#xf…

PHP、一:概述

1.概念 2.wampsever安装 百度搜索直接下载 下图是解压后目录&#xff0c;所写文件必须写在www文件夹下。 例&#xff1a;www文件夹下新建1.php&#xff0c;phpinfo()查看当前版本等信息。 使用localhost访问 php版本切换&#xff1a; 鼠标左键点击wampserver&#xff0c;切…

git rebase

git rebase rebase 是一个……我觉得很麻烦的指令&#xff0c;不过没办法&#xff0c;公司算是有个软规定必须要使用 rebase。 rebase 的功能和 merge 很像&#xff0c;不过它能够保持一个相对干净的历史&#xff0c;继续举个例子&#xff0c;假设现在有一个新的功能开发好了…

Golang Gin HTTP 请求和参数解析

gin 网络请求与路由处理 我们介绍了Gin框架&#xff0c;并做了Gin框架的安装&#xff0c;完成了第一个Gin工程的创建。 创建Engine 在gin框架中&#xff0c;Engine被定义成为一个结构体&#xff0c;Engine代表gin框架的一个结构体定义&#xff0c;其中包含了路由组、中间件、…