C++之多态性

C++基础:什么是C++的多态性 多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。 C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。(这里我觉得要补充,重写的话可以有两种,直接重写成员函数和重写虚函数,只有重写了虚函数的才能算作是体现了C++多态性)而重载则是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者 阅读详情

1.初探多态性

在面向对象方法中,所谓多态性就是不同对象收到相同消息,产生不同的行为。在C++程序设计中,多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,这样就可以用同一个函数名调用不同内容的函数。换言之,可以用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。

事实上,在程序设计中经常会使用到多态性。最简单的例子就是运算符了,例如我们使用运算符+,就可以实现整型数、浮点数、双精度类型之间的加法运算,这三种类型的加法操作其实是互不相同的,是由不同内容的函数实现的。这个例子就是使用了多态的特征。

在C++中,多态性的实现和联编(也称绑定)这一概念有关。一个源程序经过编译、链接,成为可执行文件的过程是把可执行代码联编(或称装配)在一起的过程。其中在运行之前就完成的联编成为静态联编(前期联编);而在程序运行之时才完成的联编叫动态联编(后期联编)。

静态联编支持的多态性称为编译时多态性(静态多态性)。在C++中,编译时多态性是通过函数重载和模板实现的。利用函数重载机制,在调用同名函数时,编译系统会根据实参的具体情况确定索要调用的是哪个函数。

动态联编所支持的多态性称为运行时多态(动态多态)。在C++中,运行时多态性是通过虚函数来实现的。

再举一个通俗易懂的例子:比如买票这个行为,普通人买是全价;学生买是半价票等。

2.多态的定义和实现

2.1 多态定义构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person.Person买票就是全价,而Student买票就是半价。

那么在继承中要构成多态还需要两个条件:
a. 调用函数的对象必须是指针或者引用。
b. 被调用的函数必须是虚函数,且完成了虚函数的重写。

什么是虚函数?
虚函数:在类的成员函数前加virtual关键字。

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

什么是虚函数的重写?
虚函数的重写:派生类中有一个跟基类的完全相同的虚函数,我们就称子类的虚函数重写了基类的虚函数。“完全相同”是指:函数名、参数、返回值都相同。另外,虚函数的重写也叫做虚函数的覆盖。

示例代码:

#include <iostream>
#include <stdlib.h>

using namespace std;

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 ps;
	Student st;

	Func(ps);
	Func(st);

	system("pause");
	return 0;
}

不规范的重写行为

在派生类中重写的成员函数可以不加virtual关键字,也是构成重写,因为继承后基类的虚函数被继承下来,在派生类中依旧保持虚函数的属性,我们只是重写了它。这是非常不规范的,在平时尽量不要这样使用。

注意:若子类中的函数有virtual修饰,而父类中没有,则会构成函数隐藏。

基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明基类的析构函数最好写成虚函数。

这里贴一个链接,专门解释了为什么基类的析构函数最好写成虚函数:https://blog.youkuaiyun.com/komtao520/article/details/82424468

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

3. 抽象类

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

示例代码:

#include<iostream>
#include <stdlib.h>
using namespace std;

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

int main()
{
	Test();
	system("pause");
	return 0;
}

结果: Benz-舒适
BMW-操控

4. 多态的原理

4.1 虚函数表

//计算sizeof(b)为多少???
#include<iostream>
#include <stdlib.h>
using namespace std;

class Base
{
public:
	virtual void Fun1(){
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

int main()
{
	Base b;
	cout << "sizeof(b):" << sizeof(b) << endl;
	system("pause");
	return 0;
}

在这里插入图片描述
通过测试我们发现sizeof(Base)大小为8字节。除了_b成员,还多了一个_vfptr放在对象的前面(注意有些平台可能会放在对象的后面,这个跟平台有关),对象中的这个指针我们称它为虚函数表指针。一个含有虚函数的类中至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表(虚表)中。

示例代码:

//1.增加一个派生类Derive去继承Base
//2.Derive中重写Func1
//3.Base再增加一个虚函数Fun2和一个普通函数Fun3
class Base
{
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}

	virtual void Func2(){
		cout << "Base::Func2()" << endl;
	}

	void Func3(){
		cout << "Base::Func3()" << endl;
	}

private:
	int _b = 1;
};

class Derive : public Base
{
public:
	virtual void Func1(){
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
	system("pause");
	return 0;
}

这里我们打开监视窗口,不难发现以下几点问题:
在这里插入图片描述
在这里插入图片描述
a. 派生类对象也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,另一部分是自己的成员。
b.基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫覆盖。
c.另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但不是虚函数,所以不放在虚表中。
d.虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面放了一个nullptr。
总结:派生类的虚表生成:
(1)先将基类中的虚表内容拷贝一份到派生类虚表中;
(2)如果派生类重写了基类中的某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数;
(3)派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类的虚表的最后。

多态实现的原理:
分析了这么多,我们依旧拿上边“买票”为例,Func函数传Person调用的Person::BuyTicket,传Student调用的是Student::BuyTicket.

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 John;
	Func(John);

	system("pause");
	return 0;
}

在这里插入图片描述
这样就实现了不同对象去完成同一行为时,展现出不同的形态。

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

下边给大家贴了一个链接,这个博文更好地讲解了多态,希望对大家有所帮助。
https://blog.youkuaiyun.com/u012630961/article/details/81226351

浅谈C++多态性 C++编程语言是一款应用广泛,支持多种程序设计的计算机编程语言。我们今天就会为大家详细介绍其中C++多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握。 多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphism),字面意思多种形状。   C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函... 阅读详情

相关推荐

[C++多态性 cpp] -- 让你的代码更加灵活多变

多态性是面向对象编程中的一个重要概念,它允许一个基类指针能够指向派生类的对象,并且能够调用该派生类中的成员函数,从而实现对对象的多种操作。静态多态性是通过函数重载和运算符重载来实现的,而动态多态性则是通过虚函数来实现的。当基类指针指向一个派生类对象时,如果该派生类中有与基类中的虚函数同名的函数,那么就会调用该派生类中的函数,而不是基类中的函数。通过对虚函数的重写,可以让基类指针指向派生类的对象,并且能够调用该派生类中的成员函数,从而实现对对象的多种操作。在C++中,多态性是通过虚函数来实现的。

持续更新 118

【C++】—— 多态

多态基本概念的梳理,结合图例能更好的理解多态

M的博客 1万+

【经典C语言程序100例之十八 cpp】—— C++中的多态性

在主函数中,首先创建了Rectangle和Triangle对象,然后定义了一个指向Shape类的指针shape,并分别让它指向Rectangle和Triangle对象。最后,分别通过shape->area()调用他们的area函数,输出不同的结果。上述程序中,首先定义了一个父类Shape,它有两个数据成员width和height,以及一个虚函数area。如果在父类中调用该虚函数,那么将会根据实际对象类型的不同而有不同的结果。通过使用虚函数,我们可以根据对象的实际类型来调用不同版本的函数,从而实现多态性。

CodeWhizZ的博客 157

C++多态(超级详细版)

在继承中要构成多态还有两个条件:1. 必须通过基类的指针或者引用调用虚函数2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写public:virtual void BuyTicket() { cout

kkbca的博客 2万+

S32K144之芯片锁死解锁——关闭debug端口

S32K144之芯片锁死及解锁 经常捣鼓单片机就会发现,时常会因为某些操作不当导致芯片锁死情况的发生,通过Jlink我们可以读到芯片的ID,但是无法进行Debug,这很是让人苦恼。 基于S32K144的芯片锁死,一般有如下几种可能: 1、时钟配置异常,过高的配置时钟频率以及不配置时钟,都有可能造成芯片保护锁死,我就曾经因为不注意把时钟配置代码注释掉,导致芯片锁死。 2、我是微软雅黑烧写断电,如果在...

weixin_41935674的博客 1万+

MounRiver避坑实录:手把手迁移CH32V307 EVT工程到自定义目录

本文详细记录了将沁恒CH32V307的EVT工程迁移到自定义目录的完整流程与避坑指南。针对MounRiver Studio的虚拟链接机制,重点讲解了如何清理旧配置、重新设置头文件路径、链接脚本等关键步骤,帮助开发者创建独立、可维护的MCU项目工程。

uber9的博客 244

【C++】多态

文章目录概念一、多态的定义及实现1.多态的构成条件2.virtual关键字和虚函数3.虚函数重写4.虚函数重写的三个例外(1)协变(2)析构函数重写①不需要重写虚构函数②需要重写析构函数(3)子类的虚函数可以不用virtual修饰5.final6.override二、抽象类1.概念2.接口继承和实现继承2.读入数据总结 概念 通俗来说就是多种形态,具体一些就是对于某个行为,当不同的对象去完成时会产生出不同的状态。 再进一步到C++中,就是函数调用的多种形态,这一特性可以让我们在调用函数时更加灵活。 多态又

weixin_51983604的博客 7456

科研绘图(二):R 语言实现小鼠脑图谱 3D 渲染,附完整代码与数据获取指南

在生物医学科研中,脑区的 3D 可视化是呈现实验结果、解析脑结构功能的重要手段。R 语言凭借rgl和Rvcg等工具包,可高效实现小鼠全脑及特定脑区(如 CA1 区)的立体渲染,且代码可复用性强。本文将从环境搭建、数据准备、代码实现到结果导出,手把手带你完成小鼠脑图谱 3D 绘图,小白也能快速上手!

good_rrrrr的博客 2024

C++中的多态

前言 我认为多态是因为有了虚函数,通过继承,派生类可以重写虚函数,才有了C++的多态。所以要了解多态的原理,需要重点了解虚函数和虚基表。 一.多态的概念

weixin_57023347的博客 1万+

CESM笔记——CESM1.0-CESM1.2.2-CESM2.1.3三个版本模式工作流对比

最近测试了CESM1.0,CESM1.2.2和CESM2.1.3三个版本,不同版本在workflow上有一些区别,来做点笔记。 CESM1.0 测试compset:B_2000,F_2000 workflow: 1. create_newcase -case ~/CESM/cesm1_0/script/B_2000 -compset B_2000 -res f19_g16 -mach tianhe 2. cd B_2000 (optional) env_mach_pes.xml ##修改核数,

砍柴姑娘Jourosy的博客 4944

一套C#接口通吃安川/发那科/库卡:工业机器人统一控制框架设计与产线踩坑实录

我们把机器人的通用能力抽象为接口,涵盖连接、运动、IO、寄存器、状态、报警六大类能力,所有品牌适配器都必须实现这个接口。/// 工业机器人统一控制接口// 基础连接// 运动控制// IO与寄存器// 状态与报警// 事件工业机器人统一控制框架的本质,不是把所有品牌的功能都做平,而是找到通用能力的最大公约数,通过分层抽象把品牌差异隔离在底层。上层业务只关注工艺和流程,不需要关心机器人是什么品牌、用什么SDK、通信协议是什么。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 404

C++多态性定义、实现以及示例详细介绍

C++多态性是面向对象程序设计中的重要概念,它允许子类对象具备父类对象的方法和属性。本文将详细介绍C++多态性的定义、实现以及示例,帮助初学者更好地理解和掌握多态性。

Python966的博客 5762

【OS】AUTOSAR架构下MCAL Modules软件分区问题分析

AUTOSAR架构下MCAL Modules软件分区问题分析

qq_36056498的博客 939

C++多态性思考

前沿 今天看android的服务源码时候,看见了一个C++的多态性问题,这里贴出代码,不做讲解 //这是main.cpp #include <iostream> #include "mul.hh" using namespace std; int main(void){ mul_string(); return 0; } /*这是mul.cpp*/ #include <iostream> using namespace std; int mul_string(cons

madannasf的博客 193

Yocto系列讲解[入门篇] 2 - 演示运行qemux86-64虚拟机

手把手教大家怎么编译Yocto,并运行一个qemux86虚拟机

fulinux的博客 1万+

Arcgis中的模型构建器(ModelBuilder)技术之批量图层转KML

这段时间全省各地都在开展耕地流出图斑整改恢复工作,期间或多或少都需要村委会协助摸排举证,除了举证软件外,奥维、外业精灵等软件想必大家接触得也很多。这就经常需要我们转kmz文件给业主或者摸排人员,那么有没有什么方法在arcgis下快速批量转kmz文件呢,今天就介绍一下如何在模型构建器(ModelBuilder)构建批量图层转KML的模型。

weixin_44671081的博客 1325

C++ 多态性有哪些

C++多态性有哪些?       概念:指相同的对象收到不同的消息或者不同的对象收到相同的消息时产生的不同的实现动作。 C++支持两种多态:编译时多态(静态)、运行时多态(动态) (1)编译时多态:就是在编译期确定的一种多态。在C++中主要体现在函数模板,这里需要注意,函数重载和多态无关,很多地方把函数重载误认为是编译多态,这是错误的???      举例说明一下函数模板是如何体

YF_Li123的博客 1万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

C++多态性

转自:http://blog.youkuaiyun.com/hackbuteer1/article/details/7475622 C++编程语言是一款应用广泛,支持多种程序设计的计算机编程语言。我们今天就会为大家详细介绍其中C++多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握。   多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核

song_hui_xiang 1649

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

C++的多态性

C++的多态性分为两种,一是运行时的多态性,二是编译时的多态性,这里我就不多介绍了。代码才是做好的说明,在代码中有注释以及我的理解。不知道自己这样解释是否正确。代码如下: //**********************************************-------------------------------------------************// //运行时的

fsjaky的专栏 1324

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【Journey to the Cpp】Series之多态性与虚函数

关卡三:多态性与虚函数

m0_46308522的博客 754

c++的多态性

C++编程语言是一款应用广泛,支持多种程序设计的计算机编程语言。我们今天就会为大家详细介绍其中C++多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握。   多态性可以简单地概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象编程领域的核心概念。多态(polymorphisn),字面意思多种形状。   C++多态性是通过虚函数来实现的,虚函数允许子类重新定

学历只能代表过去,能力代表着现在,而学习代表的是未来。 337
上一篇: C++中的继承
下一篇: Linux之进程信号
Romeo  i
博客等级 码龄9年 151粉丝 80原创
评论 15
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值