C++——多态 上

news2025/3/12 9:56:14

目录

一、概念

二、多态的定义及实现

三、动态多态的实现条件

四、重写相关

五、构成重写与同名隐藏的函数有什么区别?

六、C++11 中的override 和 final


一、概念

通俗来说,就是多种形态,当完成某个行为时,当不同的对象去完成时会产生不同的状态

比如买票,普通人买票就是全票,学生买票就是半票,而军人就有可能会免票。

比如刷抖音,小红刷到的大多都是穿搭帅哥明星,小黑刷到的大多都是王者吃鸡,小刚刷到的大多都是健身视频,对于同一个行为,不同得人最终刷到的视频内容大相径庭,这就是多态。

二、多态的定义及实现

下面这是之前写过的动物类之间的继承,Animal为基类,Cat和Dog为由Animal继承下来的俩个派生类。

class Animal
{
public:
	Animal(const string& name, const string& sex, int age)
		: _name(name)
		, _sex(sex)
		, _age(age)
	{}
	void sleep()
	{
		cout << _name << "在睡觉~~~" << endl;
	}
	void eat()
	{
		cout << _name << "吃东西……" << endl;
	}
protected:
	string _name;
	string _sex;
	int _age;
};

class Dog : public Animal
{
public:
	Dog(const string& name, const string& sex, int age, const string& color)
		: Animal(name, sex, age)
		, _color(color)
	{}
private:
	string _color;
};

class Cat : public Animal
{
public:
	Cat(const string& name, const string& sex, int age, const string& Tempter)
		: Animal(name, sex, age)
		, _Tempter(Tempter)
	{}
private:
	string _Tempter;
};
void Test1()
{
	Dog dog("小黑", "公", 2, "金黄色");
	dog.eat();
	dog.sleep();

	Cat cat("咪咪", "母", 1, "白色");
	cat.eat();
	cat.sleep();
}

int main()
{
	Test1();
	return 0;
}

代码的执行中,cat和dog最终的sleep与eat都是打印的同一个结果,因为他们都是继承自基类,都使用的是基类Animal中的同一个成员函数,猫和狗二者的习性是大相径庭的,所以不应该这样笼统的让它们进行同样的行为。

所以给Cat类与Dog类中添加了各自的成员函数,由于派生类中与基类都出现了相同名字的成员,则触发被动——同名隐藏,因此即会使用派生类中的成员函数。

Dog:
    void eat()
	{
		cout << _name << "吃的声音很大" << endl;
	}
	void sleep()
	{
		cout << _name << "呼噜噜……" << endl;
	}
    
Cat:
    void eat()
	{
		cout << _name << "悄咪咪的吃东西" << endl;
	}
	void sleep()
	{
		cout << _name << "zzz……" << endl;
	}

功能是实现了,可是在与用户接触的Test函数中却书写了大量重复的代码

 使用多态

第一步 使用基类引用或指针给子类赋值

void Dynamic(Animal& s)
{
	s.eat();
	s.sleep();
}
int main()
{
	Dog dog("小黑", "公", 2, "金黄色");
	Dynamic(dog);

	Cat cat("咪咪", "母", 1, "白色");
	Dynamic(cat);
	return 0;
}

第二步 将基类Animal中成员函数声明为虚函数

    virtual void sleep()
	{
		cout << _name << "在睡觉~~~" << endl;
	}
	virtual void eat()
	{
		cout << _name << "吃东西……" << endl;
	}

完成!!!

需求:实现一个绘图软件

           如果是圆,画圆

           如果是矩形,画矩形

           如果是三角形,画三角形

class Shape
{
public:
	virtual void Drow()
	{
		cout << "图形未知,无法作图" << endl;
	}
	virtual double GetPerimeter()
	{
		cout << "图形未知" << endl;
		return 0;
	}
};

class Circle : public Shape
{
public:
	Circle(double r)
		: _r(r)
	{}
	virtual void Drow()
	{
		cout << "○" << endl;
	}
	virtual double GetPerimeter()
	{
		return 2 * 3.14 * _r;
	}
private:
	double _r;
};

class Rectangle : public Shape
{
public:
	Rectangle(double length, double width)
		: _length(length)
		, _width(width)
	{

	}
	virtual void Drow()
	{
		cout << "□" << endl;
	}
	virtual double GetPerimeter()
	{
		return 2 * (_length+_width);
	}
private:
	double _length;
	double _width;
};

class Trangle : public Shape
{
public:
	Trangle(double a, double b, double c)
		: _a(a)
		, _b(b)
		, _c(c)
	{}
	virtual void Drow()
	{
		cout << "△" << endl;
	}
	virtual double GetPerimeter()
	{
		return _a+_b+_c;
	}
private:
	double _a;
	double _b;
	double _c;
};

void Test(Shape& s)
{
	s.Drow();
	cout << s.GetPerimeter() << endl;
}
int main()
{
	Circle c(2.2);
	Rectangle r(1, 2);
	Trangle t(3, 4, 5);
	Test(c);
	Test(r);
	Test(t);
	return 0;
}

 三、动态多态的实现条件

1)必须在继承前提下,子类必须重写基类的虚函数 (被virtual修饰的函数即为虚函数)

2)关于虚函数的调用,通过引用或者指针

3)动态:即只有当程序运行的时候根据传入的对象编译器才会选择类中对应的虚函数进行调用

如果类中哪个方法想要实现多态的效果,则该方法必须为虚函数,并且在子类中必须要被重写

 四、重写相关

1)基类中要被重写的成员函数必须为虚函数

2)子类虚函数与基类虚函数的原型要一致(返回值类型、函数名、参数列表必须完全一致)

        特例:

                ① 析构函数一般定义为虚函数(基类与子类函数名不同 但构成重写)

                ② 协变:基类函数返回基类的指针或引用,子类返回子类的指针或引用

                  (返回值类型不同,但也构成重写)

3)子类中的virtual关键字有没有均可(推荐加上)

4)基类与子类的虚函数访问权限可以不同(一般保持一致)

class B
{
public:
	virtual ~B()	//特例 ① virtual修饰基类的析构函数
	{
		cout << "B::~B()" << endl;
	}
	virtual B* GetObjPtr()	//特例 ② 基类返回基类的指针,子类返回子类的指针
	{
		return this;
	}
};

class D : public B
{
public:
	~D()
	{
		cout << "D::~D()" << endl;
	}
	virtual D* GetObjPtr()
	{
		return this;
	}
};

void Test(B* t)
{
	delete t;
	t = nullptr;
}

void TestAddr(B* t)
{
	if (t)
	{
		t->GetObjPtr();
	}
}

int main()
{
	B *pb = new B;
	Test(pb);

	D* pd = new D;
	Test(pd);
	
///

	B b;
	TestAddr(&b);
	D d;
	TestAddr(&d);

	return 0;
}

五、构成重写与同名隐藏的函数有什么区别?

1)相同点:

        1、都在同一个继承体系下,一个在基类中,一个在子类中

        2、二者的函数名相同

2)不同点:

        1、重写中基类的函数必须是虚函数,而同名隐藏没有要求

        2、重写要求俩个函数的原型必须一致(析构函数与协变除外)

             而同名隐藏只要求俩个函数函数名相同

下列代码只有func1构成重写

class B
{
public:
	virtual void func1()
	{
		cout << "B::func1" << endl;
	}
	void func2()
	{
		cout << "B::func2" << endl;
	}
	void func3()
	{
		cout << "B::func3" << endl;
	}
	void func4(int a)
	{
		cout << "B::func3" << endl;
	}
};

class D : public B
{
public:
	virtual void func1()
	{
		cout << "D::func1" << endl;
	}
	void func2()
	{
		cout << "D::func2" << endl;
	}
	virtual void func3()	// 子类中为虚函数 基类中不为虚函数	(不构成重写)
	{
		cout << "D::func3" << endl;
	}
	virtual void func4(int a)	// 子类中为虚函数 基类中不为虚函数	(不构成重写)
	{
		cout << "D::func4" << endl;
	}
};

六、C++11 中的override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数
名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有
得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮
助用户检测是否重写

        final修饰类,表示该类不能被继承。

1)final修饰虚函数,表示该虚函数不能再被重写(最后一个了嘛)

2)override只能修饰子类中的虚函数指定该虚函数必须要实现重写基类中函数(如果无法实现则报错)

 

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

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

相关文章

Python 手写数字识别 MNIST数据集下载失败

目录 一、MNIST数据集下载失败 1 失败的解决办法&#xff08;经验教训&#xff09;&#xff1a; 2 亲测有效的解决方法&#xff1a; 一、MNIST数据集下载失败 场景复现&#xff1a;想要pytorchMINIST数据集来实现手写数字识别&#xff0c;首先就是进行MNIST数据集的下载&am…

Fastjson踩“坑”记录和“深度”学习

作者&#xff1a;陶征策 阿里国际站商家技术团队 Fastjson是阿里开发的Java语言编写的高性能JSON库&#xff0c;本文总结了Fastjson使用时的一些注意事项&#xff0c;并简单分析了Fastjson的底层工作原理&#xff0c;结合具体的验证代码以及跟Jackson的对比&#xff0c;希望能…

非代码的贡献也能成为Committer,我与DolphinScheduler社区的故事

点亮 ⭐️ Star 照亮开源之路 https://github.com/apache/dolphinscheduler ​ // 每个人对于”开源社区“的定义都不一样&#xff0c;在社区的想法也完全不一样&#xff0c;我认为玩开源就像”谈恋爱“&#xff0c;要想方设法对它好&#xff0c;在接触 Apache DolphinSche…

nodejs+vue毕业生求职招聘平台系统

前台首页功能模块毕业生信息招聘平台首页、空中宣讲会、招聘岗位、求职信息、论坛信息、试卷列表、招聘资讯、个人中心、后台管理功能。论坛中心试卷列表招聘资讯管理员功能模块管理员登录空中宣讲会管理招聘岗位管理毕业生管理企业功能模块招聘岗位管理信息咨询管理线上面试管…

java实现二叉树(一文带你详细了解二叉树的)

&#x1f387;&#x1f387;&#x1f387;作者&#xff1a; 小鱼不会骑车 &#x1f386;&#x1f386;&#x1f386;专栏&#xff1a; 《数据结构》 &#x1f393;&#x1f393;&#x1f393;个人简介&#xff1a; 一名专科大一在读的小比特&#xff0c;努力学习编程是我唯一…

JDBC与Druid连接池

1 什么是jdbc? JDBC&#xff08;java database connectivity &#xff09;是Java语言连接操作关系型数据库的一套解决方案&#xff0c;屏蔽了底层各数据库不同的差异。 具体是通过sun公司定义的统一的一套API【标准接口】来实现解决差异&#xff0c; 具体实现是各大数据库厂商…

【数据库】 mysql的四种安装方式

目录 MySQL 安装部署 一&#xff0c;仓库安装 二&#xff0c;本地安装 三&#xff0c;容器安装 四&#xff0c;源码安装 MySQL 安装部署 一&#xff0c;仓库安装 1&#xff0c;使用rpm命令从指定的网址装包 ​ [rootlocalhost yum.repos.d]# rpm -ivh https://repo.mys…

每日学术速递2.10

Subjects: cs.Cv 1.Spatiotemporal Deformation Perception for Fisheye Video Rectification 标题&#xff1a;鱼眼视频矫正的时空形变感知 作者&#xff1a;Shangrong Yang, Chunyu Lin, Kang Liao, Yao Zhao 文章链接&#xff1a;https://arxiv.org/abs/2302.03934v1 项…

【手写 Vuex 源码】第四篇 - Vuex 中 Getters 的实现

一&#xff0c;前言 上篇&#xff0c;主要介绍了 Vuex 中 State 状态的实现&#xff0c;主要涉及以下几个点&#xff1a; 创建 Store 类中的 State 状态&#xff1b;借助 Vue 实现 State 状态的响应式&#xff1b; 本篇&#xff0c;继续介绍 Vuex 中 getters 的实现&#xf…

VHDL语言基础-时序逻辑电路-寄存器

目录 寄存器的设计&#xff1a; 多位寄存器&#xff1a; 多位寄存器的VHDL描述: 移位寄存器&#xff1a; 串进并出的移位寄存器的VHDL描述: 寄存器的设计&#xff1a; 多位寄存器&#xff1a; 一个D触发器就是一位寄存器&#xff0c;如果需要多位寄存器&…

飞凌嵌入式RK3568J核心板助力工业机器人产业迈向高质量发展新阶段

工业机器人是能够代替人工完成高强度重复工作的多自由度机器装置&#xff0c;不仅可以确保产品质量&#xff0c;还可以大幅提高生产效率。据工信部数据显示&#xff0c;“十三五”期间我国工业机器人产量从7.2万套增长到了21.2万套&#xff0c;年均增长31%&#xff0c;预计2023…

切换分支报错:Untracked Files Prevent Checkout

切换分支报错&#xff1a;Untracked Files Prevent Checkoutgit分支切换 Untracked Files Prevent Checkout本人解决办法&#xff1a;git分支切换 Untracked Files Prevent Checkout 新起的项目在切换master分支到工作分支时&#xff0c;出现下图的问题&#xff1a; Untracked…

【机器学习】过拟合与正则化

上一章——逻辑回归 文章目录三种拟合状态解决过拟合的三种方法什么是正则化正则化的数学原理线性回归恭喜三种拟合状态 在之前的课程中&#xff0c;我们说过机器学习的中极为重要的一步&#xff0c;就是给训练集找到一条合适的拟合曲线。 还是以房价问题这个回归问题为例&…

【微服务】微服务架构超强讲解,通俗易懂

微服务架构目录一、微服务架构介绍二、出现和发展三、传统开发模式和微服务的区别四、微服务的具体特征五、面向服务的架构SOA&#xff08;service oriented architecture&#xff09;和微服务的区别1、SOA喜欢重用&#xff0c;微服务喜欢重写2、SOA喜欢水平服务&#xff0c;微…

Linux教程:MQTT入门基础概念与学习介绍及服务部署搭建并使用桌面工具进行测试开发

前言&#xff1a; ----在2023年的今天&#xff0c;智能家居与智能家电的兴起犹如滚滚长江迅速袭来&#xff0c;智能终端设备也不断出现在人们的视野当中&#xff0c;实现远程控制&#xff0c;其中必然不能缺少终端与终端&#xff0c;终端与服务之间的交互&#xff0c;如何来解…

ag-Grid Enterprise

ag-Grid Enterprise Ag-Grid被描述为一种商业产品&#xff0c;已在EULA下分发&#xff0c;它非常先进&#xff0c;性能就像Row分组一样&#xff0c;还有范围选择、master和case、行的服务器端模型等等。 ag Grid Enterprise的巨大特点&#xff1a; 它具有以下功能和属性&#x…

Docker调用Intel集显实现FFmpeg硬解码

文章目录Docker调用Intel集显实现FFmpeg硬解码参考FFmpeg 集成qsv方式一 容器完成所有步骤方式二 容器完成部分步骤方式三 dockerfile部署Docker调用Intel集显实现FFmpeg硬解码 参考 ffmpeg_qsv_docker拉取该镜像可以实现FFmpeg集成vaapi的硬加速&#xff0c;通过dockerfile文…

什么是特权访问管理(PAM)

特权访问管理 &#xff08;PAM&#xff09; 是指一组 IT 安全管理原则&#xff0c;可帮助企业隔离和管理特权访问、管理特权帐户和凭据、控制谁可以获得对哪些端点的管理访问权限级别&#xff0c;并监视用户对该访问权限执行的操作。 什么是特权访问 特权访问是一种 IT 系统访…

2023.2.10学习记录Docker容器

Docker 必须跑在Linux内核上 镜像是一个轻量级可执行的独立软件包 新建一个docker容器只需要几秒钟 Docker常用命令 启动类命令 镜像命令 容器命令 docker images docker search --limit 5 redis docker pull redis:6.0.8 docker system df 查看镜像/容器/…

使用QT中的绘画工具与定时器工具实现简易时钟

需求&#xff1a;使用QT中的绘画工具与定时器工具实现简易时钟代码实现过程&#xff1a;widget.h#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QPainter> //引入画家类 #include<QPaintEvent> //引入绘制事件类 #include&l…