c++多态的定义和原理

目录

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

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

武夷山细节决定成败抓质量求生存

在当今竞争激烈的市场环境中&#xff0c;细节决定成败&#xff0c;质量求生存的理念已成为企业发展的关键。蓝鹏测控科技有限公司&#xff0c;一家专业从事工业测量领域的高新技术企业&#xff0c;正是秉持这一理念&#xff0c;在工业测径仪领域取得了显著成就。 蓝鹏测控科技…

Ozon俄罗斯哪些产品热销中?Ozon7月市场热卖趋势放送

Ozon俄罗斯哪些产品热销工具&#xff1a;D。DDqbt。COm/74rD 据Ozon数据&#xff0c;2023年&#xff0c;在自提服务方面&#xff0c;Ozon投资了100亿扩展自提网络&#xff0c;自提点数量激增至超过5万个&#xff0c;是之前的2.6倍。 物流基础设施方面&#xff0c;Ozon在仓库建…

BGP第二日

上图为今日所用拓扑 &#xff0c;其中R1和R4&#xff0c;R3和R5为EBGP邻居&#xff0c;R1和R3为IBGP邻居&#xff0c;AS200区域做OSPF动态路由 一.BGP建立邻居的六种状态 1.idle 空闲状态&#xff1a;建立邻居最初的状态 2.Connect 连接状态&#xff1a;在…

360安全浏览器就是不行-python秒破解

下面画框都很容易破解&#xff0c;大家试试

ZGC在三色指针中的应用

ZGC基于颜色指针的并发处理算法 ZGC初始化之后&#xff0c;整个内存空间的地址视图被设置为Remapped&#xff0c;当进入标记阶段时的视图转变为Marked0&#xff08;也称为M0&#xff09;或者Marked1&#xff08;也称为M1&#xff09;&#xff0c;从标记阶段结束进入转移阶段时…

怎么样的主食冻干算好冻干?品质卓越、安全可靠的主食冻干分享

当前主食冻干市场产品质量参差不齐。一些品牌过于追求营养数据的堆砌和利润的增长&#xff0c;却忽视了猫咪健康饮食的基本原则&#xff0c;导致市场上出现了以肉粉冒充鲜肉、修改产品日期等不诚信行为。更令人担忧的是&#xff0c;部分产品未经过严格的第三方质量检测便上市销…

MATLAB中的SDPT3、LMILab、SeDuMi工具箱

MATLAB中的SDPT3、LMILab、SeDuMi工具箱都是用于解决特定数学优化问题的工具箱&#xff0c;它们在控制系统设计、机器学习、信号处理等领域有广泛的应用。以下是对这三个工具箱的详细介绍&#xff1a; 1. SDPT3工具箱 简介&#xff1a; SDPT3&#xff08;Semidefinite Progra…

Jetson-AGX-Orin 非docker环境源码编译安装CyberRT

Jetson-AGX-Orin 非docker环境源码编译安装CyberRT 1、安装依赖 sudo apt update sudo apt-get install g gdb gcc cmake sudo apt install libpoco-dev uuid-dev libncurses5-dev python3-dev python3-pip python3 -m pip install protobuf3.14.02、下载CyberRT源码 git cl…

拥抱 AGI:PieDataCS 引领云原生数据计算系统新范式

自2023年后&#xff0c;人工智能技术进入了一个更为成熟和广泛应用的阶段&#xff0c;人工通用智能&#xff08;AGI&#xff09;这一概念也成为了科技界和产业界热议的焦点。本文将结合 AGI 时代背景&#xff0c;从架构设计到落地实践&#xff0c;详细介绍拓数派云原生数据计算…

Spring开发实践(四)

VO&#xff0c;BO&#xff0c;PO&#xff0c;DO&#xff0c;DTO的区别 1、PO&#xff1a;Persistant Object(持久对象)&#xff0c;基本上&#xff0c;PO对象中的属性就是对应着数据库中表的字段&#xff0c;加上⼀些get和set⽅法的组成。例&#xff1a;个⼈信息表中分别有&am…

德国哥廷根大学《Nature Geoscience》最新成果!揭示热带森林对季节性干旱的响应机制!

本文首发于“生态学者”微信公众号&#xff01; 越来越多的研究称热带森林的生产力受到养分限制&#xff0c;这可能影响其对季节性干旱的反应&#xff08;Nature正刊&#xff01;亚利桑那大学 博士生陈舒立一作兼通讯 最新重磅成果&#xff01;&#xff1b;《Nature Geoscience…

简洁易用,快速制作高品质产品册的工具

在数字化时代&#xff0c;高效制作高品质产品册的需求日益增长。市场上涌现出众多声称能够帮助快速制作产品册的工具&#xff0c;但真正能兼顾简洁易用和品质的却寥寥无几。 ​这款工具名为“FLBOOK”&#xff0c;它凭借其强大的功能和简单易用的操作界面&#xff0c;赢得了众多…

RK3568笔记三十三: helloworld 驱动测试

若该文为原创文章&#xff0c;转载请注明原文出处。 报着学习态度&#xff0c;接下来学习驱动是如何使用的&#xff0c;从简单的helloworld驱动学习起。 开始编写第一个驱动程序—helloworld 驱动。 一、环境 1、开发板&#xff1a;正点原子的ATK-DLRK3568 2、系统&#xf…

谷粒商城学习笔记-23-分布式组件-SpringCloud Alibaba-Nacos配置中心-简单示例

之前已经学习了使用Nacos作为注册中心&#xff0c;这一节学习Nacos另外一个核心功能&#xff1a;配置中心。 一&#xff0c;Nacos配置中心简介 Nacos是一个易于使用的平台&#xff0c;用于动态服务发现和配置管理。作为配置中心&#xff0c;Nacos提供了以下核心功能和优势&am…

适合初学者的嵌入式项目有哪些?

适合初学者的嵌入式项目有哪些? 嵌入式学习是一个实践性很强的领域&#xff0c;通过实际项目可以帮助你巩固理论知识并提升技能。以下是几个适合初学者练手的嵌入式项目&#xff0c;每个项目都涵盖了从硬件到软件的不同层面&#xff1a; 1.LED灯控制 详细描述&#xff1a;在…

Parallels Desktop 19 for Mac 让你的 Mac 变得无比强大

简单来说&#xff0c;Parallels Desktop 19 for Mac 可以让你在苹果 Mac 电脑 (包括 M1、M2 或 Intel) 的电脑上「同时运行」一个或多个 Windows 或 Linux 系统&#xff0c;并能随意在不同平台软件之间切换。它能让你的 Mac 变得无比强大&#xff0c;因为 PD 能让你直接在 macO…

centos下编译安装redis最新稳定版

一、目标 编译安装最新版的redis 二、安装步骤 1、redis官方下载页面 Downloads - Redis 2、下载最新版的redis源码包 注&#xff1a;此时的最新稳定版是 redis 7.2.5 wget https://download.redis.io/redis-stable.tar.gz 3、安装编译环境 yum install -y gcc gcc-c …

debian 12 Install

debian 前言 Debian是一个基于Linux内核的自由和开放源代码操作系统&#xff0c;由全球志愿者组成的Debian项目维护和开发。该项目始于1993年&#xff0c;由Ian Murdock发起&#xff0c;旨在创建一个完整的、基于Linux的自由软件操作系统。 debian download debian 百度网盘…

地理信息安全与隐私保护:守护你我位置的隐形盾牌

在数字时代&#xff0c;地理信息技术如地理信息系统&#xff08;GIS&#xff09;和全球定位系统&#xff08;GPS&#xff09;已成为日常生活不可或缺的一部分&#xff0c;它们为我们带来便利的同时&#xff0c;也悄然触及个人隐私的敏感地带。今天&#xff0c;我们就来聊聊地理…

ARMxy微电网控制新星:赋能智能电网监控

能源转型和智能电网建设高效、可靠的电力输配系统成为了行业关注的焦点。随着物联网、大数据、人工智能技术的融合应用&#xff0c;电网监控与变电站自动化系统亟需更为智能、灵活的解决方案来提升其性能与稳定性。正是在这样的背景下&#xff0c;ARM电力控制微电网主机BL340以…