c++多态的定义和原理

news2024/9/9 5:12:28

目录

1、多态的定义和实现

1.多态的构成条件

 2.虚函数

 3.虚函数的重写(覆盖)

4.虚函数重写的两个例外

5.c++11 override和final

6.重载,覆盖(重写)和隐藏(重定义)

2、抽象类

概念

接口继承和实现继承

3、多态的原理

1.虚函数表

2.多态的原理

4、多继承中的虚函数表


1、多态的定义和实现

多态的概念:多种形态去完成同一个行为,会出现不同的状态,叫做多态;

1.多态的构成条件

多态是在不同继承关系的类对象,调用同一个函数,产生的不同行为;

那么在继承中要构成多态还有两个条件:

1.必须通过基类的指针或者引用调用虚函数;

2.被调用的必须是虚函数,且派生类必须对基类的虚函数进行重写;

 2.虚函数

虚函数:被virtual修饰的是类成员函数就是虚函数;

virtual void xu()
{
    cout << "this is student" << endl;
}

 3.虚函数的重写(覆盖)

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的函数名,返回值,参数类型完全相同),称为子类虚函数重写了基类的虚函数;

class person
{
public:
	virtual void xu()
	{
		cout << "this iis person" << endl;
	}
};
class student:public person
{
public:
    void xu()
	{
		cout << "this is student" << endl;
	}
};

注意:在重写虚函数时,派生类的虚函数可以不加virtual关键字,因为派生类已经继承了基类的虚函数,依然保持虚函数属性,但是这种写法不规范,所以我们尽量不这样写;

4.虚函数重写的两个例外

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

派生类重写虚函数时,与基类虚函数的返回值不一样,基类虚函数返回基类对象的指针或引用,派生类虚函数返回派生类对象的指针和引用;这就叫做协变;

class person
{
public:
	virtual person* xu()
	{
		cout << "this iis person" << endl;
	}
};
class student:public person
{
public:
	virtual student* xu()
	{
		cout << "this is student" << endl;
	}
};

2、析构函数的重写(基类与派生类的析构函数名字不同)

如果基类析构函数为虚函数,那么派生类析构函数无论是否加virtual关键字,都与基类析构函数构成重写,虽然函数名不同,看起来违背了规则,其实这里可以理解成编译器对析构函数的名称进行了特殊处理,编译后析构函数的名称同一命名为destructor;

 p1调用基类的析构函数,p2调用派生类的析构函数,先析构派生类,再析构基类,所以p2调用了两个析构函数;

5.c++11 override和final

final:修饰虚函数,表示该虚函数不能再被重写

override :检查派生类虚函数是否重写了基类某个虚函数,如果没有重写,编译报错;

6.重载,覆盖(重写)和隐藏(重定义)

2、抽象类

概念

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

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

 虽然不能实例化父类的对象,但是可以用父类的指针或者引用,构造子类,前提是子类重写了父类的虚函数;

接口继承和实现继承

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

3、多态的原理

1.虚函数表

class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _a = 1;
	char _b = 'a';
};
int main()
{
	//在32位平台下,sizeof(Base)是多少?
	cout << sizeof(Base) << endl;
	return 0;
}

 答案是12,为什么呢?

我们调试一下发现,Base类中存了一个_vfptr指针,我们叫做虚函数表指针,一个含有虚函数的类至少会有一个虚函数表指针,因为虚函数的地址要放到虚函数表中,虚函数表也叫做虚表,

我们进行一个改造:

1.我们增加一个派生类Derive去继承Base;

2.在Derive中重写 Func1;

3.Base在增加一个虚函数Func2和普通函数Func3 ;

class Base
{
public:
	virtual void Func1() { cout << "Func1()" << endl; }
	virtual void Func2() { cout << "Func2()" << endl; }
	        void Func3() { cout << "Func3()" << endl; }
private:
	int _a = 1;
	char _b = 'a';
};
class Derive :public Base
{
public:
	virtual void Func1(){cout << "Func1()" << endl;}
private:
	int _d = 2;
};
int main()
{
	Base b;
	Derive d;

	return 0;
}

      

 通过调试发现:

1.b中有两个虚函数,所以虚函数表里存了两个地址,Func3不是虚函数,所以虚函数表里没有;

2.d中继承了基类中的虚函数,对Func1进行重写,所以d中的虚函数表中Func1的地址和基类的Func1有所不同,因为派生类对Func2没有进行重写,所以只是继承下来,并没有改变Func2的地址,Func3也继承下来了,但是没有放到虚表里;

3.虚函数表本质是一个函数指针数组,一般这种情况这个数组最后面放了一个nullptr;

4.派生类虚表的形成:先是将基类中的虚表内容拷贝一份到派生类的虚表中,如果派生类重写了基类某个虚函数,用派生类的虚函数覆盖虚表中基类的虚函数,派生类自己新增的虚函数按其在派生类中的声明顺序增加到派生类虚表的最后;

5.注意:虚函数本质还是存到代码段中,虚函数表中存的是虚函数的地址;

2.多态的原理

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
	return 0;
}

 观察上图,people指向基类对象时,p->BuyTicket()是在基类的虚表中找到虚函数是person::BuyTicket,person指向派生类对象时,p->BuyTicket()是在派生类的虚表中找到是对基类虚函数重写的函数,也就是student::BuyTicket,

因为派生类的虚函数对基类的虚函数进行了重写,所以指向父类的调用父类的虚函数,指向子类的调用子类的虚函数,由于将子类通过引用传给父类,发生了切片,所以才能产生多态的效果;

4、多继承中的虚函数表

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) ();
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;
}
int main()
{
 Derive d;
 VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
 PrintVTable(vTableb1);
 VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d+sizeof(Base1)));
 PrintVTable(vTableb2);
 return 0;
}

从下图可以就看到:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中

 

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

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

相关文章

deep learning 环境配置

1 NVIDIA驱动安装 ref link: https://blog.csdn.net/weixin_37926734/article/details/123033286 2 cuda安装 ref link: https://blog.csdn.net/qq_63379469/article/details/123319269 进去网站 https://developer.nvidia.com/cuda-toolkit-archive 选择想要安装的cuda版…

城市智慧公厕-为用户提供最直观的上厕所指引

在智慧城市的发展进程中&#xff0c;城市智慧公厕作为公共服务设施的智能化升级版&#xff0c;正悄然改变着我们日常生活中的一个基本需求——如厕体验。它不仅提升了公共卫生水平&#xff0c;还为城市居民和游客提供了前所未有的便利&#xff0c;成为展现城市文明程度和科技实…

DDR3 (四)

1 DDR3 8倍预取 DDR3相比DDR2外部IO时钟又提高了一倍&#xff0c;因此DDR3外部IO时钟是内核时钟的4倍&#xff0c;再加上双沿采样&#xff0c;因此DDR3可以实现8倍预取 2 DDR3 芯片位宽 DDR3使用8倍预取技术&#xff0c;指的是芯片位宽&#xff08;DQ数据线位宽&#xff09…

ST7789 linux4.x驱动

文章目录 ST7789 linux4.x驱动设备树配置驱动程序编译驱动测试驱动 ST7789 linux4.x驱动 设备树配置 pinctrl_ecspi2_cs_1: ecspi2_cs_grp-1 {fsl,pins <MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x40017059>; };pinctrl_ecspi2_1: escpi2grp {fsl,pins <MX6UL_PAD_CSI_…

【开发工具】webStrom2024版-永久使用

1、解压文件 2、安装步骤 先执行unistall-current-user.vbs&#xff0c;确保当前环境变量下没有历史使用记录。再执行install-current-user.vbs。运行的时候&#xff0c;会有第一个弹窗&#xff0c;点击确定&#xff0c;稍微等待一会&#xff0c;会出现 Done 的弹窗&#xff0…

前端最全面试题【最新版本2024-7月】

文章目录 最常见问题javascript篇Javascript的运行机制javascript的数据类型怎样判断变量的类型数据类型转换闭包的优缺点v-if和v-for哪个优先级更高&#xff1f; 如果两个同时出现&#xff0c;应该怎么优化得到更好的性能&#xff1f;HTML5的新特性和CSS3的新特性div 上下居中…

流失人数月度统计教程

文章目录 一、ABC轮线1、数据源&#xff1a;每个月最后一天数据2、填写 "B轮&#xff08;直播间&#xff09;-流失人数(个人数据)" 一、ABC轮线 以B轮直播间数据填写为例 1、数据源&#xff1a;每个月最后一天数据 如7月&#xff0c;即为7月31日 2、填写 “B轮&…

x264 编码器 AArch64 汇编函数模块关系分析

x264 编码器 AArch64 汇编介绍 x264 是一个流行的开源视频编码器,它实现了 H.264/MPEG-4 AVC 标准。x264 项目致力于提供一个高性能、高质量的编码器,支持多种平台和架构。对于 AArch64(即 64 位 ARM 架构),x264 编码器利用该架构的特性来优化编码过程。在 x264 编码器中,…

Wireshark 对 https 请求抓包并展示为明文

文章目录 1、目标2、环境准备3、Wireshark 基本使用4、操作步骤4.1、彻底关闭 Chrome 进程4.2、配置 SSLKEYLOGFILE [核心步骤]4.3、把文件路径配置到 Wireshark 指定位置4.4、在浏览器发起请求4.5、抓包配置4.6、过滤4.6.1、过滤域名 http.host contains "baidu.com4.6.2…

【Linux】进程间通信之System V共享内存

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

凯中精密:下一个正丹吗?

业绩预增超十倍&#xff01; 又一匹A股业绩黑马诞生——凯中精密 近期&#xff0c;凯中精密发布2024年上半年业绩预告&#xff0c;预计净利润增速高达1068%至1402%。 从23年的209.54%到24年Q1惊人的6885.78%&#xff0c;再到24年上半年的十倍增速&#xff0c;这条业绩黑马利润…

HNU-2024操作系统实验-Lab9-Shell

一、 实验目的 理解Shell程序的原理、底层逻辑和Shell依赖的数据结构等 在操作系统内核MiniEuler上实现一个可用的Shell程序 能够根据相关原理编写一条可用的Shell指令 二、 实验过程 首先从底层出发&#xff0c;实现Shell程序 1.在src/include目录下新建prt_shell.h头文…

Splashtop 在医疗与制药领域的业务增长近五倍

2024年7月10日 加利福尼亚州库比蒂诺 Splashtop 是安全远程访问和 IT 支持解决方案领域的领先企业&#xff0c;该公司今天宣布&#xff0c;在医疗与制药领域业务同比增长492%&#xff0c;取得了里程碑式的成就。快速发展的数字实验室环境和持续的网络安全威胁需要实施无缝、安…

AWS无服务器 应用程序开发—第十七章 Application Composer

Application Composer 是 AWS 提供的一种可视化工具&#xff0c;用于设计和构建无服务器应用程序。它通过拖放界面简化了无服务器架构的创建过程&#xff0c;使开发者能够更直观地设计和配置应用程序的各个组件。 主要功能 可视化设计 通过拖放界面&#xff0c;开发者可以轻…

科技与水利的完美融合:从数据采集到智能决策,全面解析智慧水利解决方案如何助力水利行业实现智能化管理

本文关键词&#xff1a;智慧水利、智慧水利工程、智慧水利发展前景、智慧水利技术、智慧水利信息化系统、智慧水利解决方案、数字水利和智慧水利、数字水利工程、数字水利建设、数字水利概念、人水和协、智慧水库、智慧水库管理平台、智慧水库建设方案、智慧水库解决方案、智慧…

如何搭建互联网医院系统源码?医疗陪诊APP开发实战详解

今天&#xff0c;小编将为大家讲解如何搭建一个完整的互联网医院系统源码&#xff0c;并介绍医疗陪诊APP的开发实战。 一、互联网医院系统的架构设计 搭建一个完整的互联网医院系统&#xff0c;需要从架构设计开始。一个典型的互联网医院系统通常包含以下几个核心模块&#xf…

钡铼ARMxy控制器在智能网关中的应用

随着IoT物联网技术的飞速发展&#xff0c;智能网关作为连接感知层与网络层的枢纽&#xff0c;可以实现感知网络和通信网络以及不同类型感知网络之间的协议转换。钡铼技术的ARMxy系列控制器凭借其高性能、低功耗和高度灵活性的特点&#xff0c;在智能网关中发挥了关键作用&#…

KEIL5 MDK的(官网)下载安装(Win11)

一、KEIL5 MDK下载 1、Keil官网下载&#xff1a;Keil Product Downloadshttps://www.keil.com/download/product/ 支持包和破解软件和V5编译器下载链接 链接&#xff1a;https://pan.baidu.com/s/1ery0Q3FAR8_bLLlPQHSFNg?pwd9pxf 提取码&#xff1a;9pxf 选择最新版本下载&…

【leetcode】滑动窗口专题

文章目录 1.长度最小的子数组2.无重复字符的最长子串3.最大连续1的个数III4.将x减小到0的最小操作数5.水果成篮6.找到字符串中所有字母异位词7.串联所有单词的子串8.最小覆盖子串 1.长度最小的子数组 leetcode 209.长度最小的子数组 看到这个题目&#xff0c;第一眼肯定想到的…

从人工巡检到智能预警:视频AI智能监控技术在水库/河湖/水利防汛抗洪中的应用

一、背景需求分析 近日&#xff0c;我国多省市遭遇连日暴雨&#xff0c;导致水库、湖泊、河道等水域水位暴涨&#xff0c;城市内涝频发。随着夏季汛期的到来&#xff0c;降雨天气频繁&#xff0c;水利安全管理面临严峻挑战。为保障水库安全、预防和减少洪涝灾害&#xff0c;采…