浅谈C++|多态篇

news2024/12/25 12:15:52


1.多态的基本概念

多态是C++面向对象三大特性之一多态分为两类
1. 静态多态:函数重载和运算符重载属于静态多态,复用函数名·

2.动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态区别:
·静态多态的函数地址早绑定–编译阶段确定函数地址

·动态多态的函数地址晚绑定–运行阶段确定函数地址下面通过案例进行讲解多态

动态多态满足条件

1、有继承关系
2、子类重写父类的虚函数

动态多态使用
父类的指针或者引用执行子类对象

重写:函数返回值类型函数名参数列表完全—致称为重写

 静态多态代码:

#include <iostream>
using namespace std;
class dongwu {
public:
	void speak() {
		cout << "动物叫" << endl;
	}
};

class cat :public dongwu {
	void speak() {
		cout << "猫叫" << endl;
	}
};
//早绑定,编译阶段就确定函数的地址

void speak(dongwu& p) {
	p.speak();
}
void fun() {
	cat p;
	speak(p);
}
int main() {
	fun();
	cat p;
	//发生隐式转换,只能把儿子转为父亲,退化
	dongwu m = p;
	m.speak();
	return 0;
}

注意转化,以及早绑定的特性是执行当前的类型的函数,不执行儿子的函数

动态动态代码: 

#include <iostream>
using namespace std;
class dongwu {
public:
	//virtual虚函数关键字
	virtual void speak() {
		cout << "动物叫" << endl;
	}
};

class cat :public dongwu {
	//子类的virtual可加可不加
	virtual void speak() {
		cout << "猫叫" << endl;
	}
};

//晚绑定,执行子类的函数
void speak(dongwu& p) {
	p.speak();
}
void fun() {
	cat p;
	speak(p);
}
int main() {
	fun();
	
	return 0;
}

二.多态的底层原理

我们先看以下代码:

#include <iostream>
using namespace std;
class father1 {
public:
	void speak() {
		cout << "动物叫" << endl;
	}
};
class father2 {
public:
	virtual void speak() {
		cout << "动物叫" << endl;
	}
};
int main() {
	cout << sizeof(father1) << endl;
	cout << sizeof(father2) << endl;

	return 0;
}

 当类的成员函数加入虚函数关键字后,会发现类的大小发生了改变。此时类的内部结构。此时类的内部会多一个指针虚函数(表)指针,虚函数指针指向虚函数表,虚函数表中存储虚函数的入口地址

那么当派生类继承基类后,如果成员函数没有重名,那么会完全继承父类的结构。 

 

 但是当派生类,重写基函数的虚函数时,派生类中的虚函数表会发生改变,此时虚函数表指向派生类的虚函数,基类的虚函数被覆盖。

 

 此时,我们有派生类隐式转换为基类时,虚函数表中的内容并不改变,此时调用虚函数,执行的是派生类的虚函数。

三.多态的优点 

1、组织结构清晰

2、可读性强

3、对于前期和后期扩展以及维护性高

 普通计算机类:

#include <iostream>
using namespace std;
class jisuanqi {
public:
	int a, b;
	int jisuan(string fu) {
		if (fu == "+") {
			return a + b;
		}
		else if (fu == "-") {
			return a - b;
		}
		else if (fu == "*") {
			return a * b;
		}
	}
};
void fun() {
	jisuanqi q;
	q.a = 200;
	q.b = 100;
	cout << q.a << " - " << q.b << " = " << q.jisuan("-") << endl;
	cout << q.a << " + " << q.b << " = " << q.jisuan("+") << endl;
	cout << q.a << " * " << q.b << " = " << q.jisuan("*") << endl;

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

 多态计算机类:

#include <iostream>
using namespace std;
class jisuanqi {
public:
	int a;
	int b;
	virtual int jisuan() {
		return 0;
	}
};

class add :public jisuanqi {
	virtual int jisuan() {
		return a+b;
	}
};

class jian :public jisuanqi {
	virtual int jisuan() {
		return a - b;
	}
};

class cheng:public jisuanqi {
	virtual int jisuan() {
		return a * b;
	}
};

void fun() {
	jisuanqi* p = new add;
	p->a = 200;
	p->b = 100;
	cout << p->a << " + " << p->b << " = " << p->jisuan()<<endl;
	delete p;
	
	p = new jian;
	p->a = 200;
	p->b = 100;
	cout << p->a << " - " << p->b << " = " << p->jisuan()<<endl;
	delete p;


	p = new cheng;
	p->a = 200;
	p->b = 100;
	cout << p->a << " * " << p->b << " = " << p->jisuan()<<endl;
	delete p;
}
int main() {
	fun();
	return 0;
}

四.纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容因此可以将虚函数改为纯虚函数。当类中有了纯虚函数,这个类也称为抽象类

纯虚函数语法: virtual    返回值类型   函数名︰(参数列表)= 0 ;

抽象类特点:
    ·无法实例化对象
    ·子类必须重写抽象类中的纯虚函数,否则也属于抽象类
 

 代码:

#include <iostream>
using namespace std;

class father {
public:
	//纯虚函数
	virtual void fun() = 0;
};


class son :public father{
public:
	void fun() {
		cout << "我是sond" << endl;
	}
};

void fun() {
	//多态f必须是指针或者引用
	//father f; 报错不可实例化
	father* f = new son;
	f->fun();
}
int main() {
	fun();
	return 0;
}

案例制作饮品:

#include <iostream>
using namespace std;
class father {
public:
	virtual void zhushui() = 0;
	virtual void chongpao() = 0;
	virtual void daoru() = 0;
	virtual void jialiao() = 0;
	void fun() {
		zhushui();
		chongpao();
		daoru();
		jialiao();
	}
};

class tea :public father{
	void zhushui() {
		cout << "煮山泉水" << endl;
	};
	void chongpao() {
		cout << "冲茶" << endl;
	};
	void daoru() {
		cout << "倒入茶杯中" << endl;
	};
	void jialiao() {
		cout << "加入枸杞" << endl;
	};
};

class kafei : public father{
	void zhushui() {
		cout << "煮水" << endl;
	};
	void chongpao() {
		cout << "冲咖啡" << endl;
	};
	void daoru() {
		cout << "倒入咖啡杯中" << endl;
	};
	void jialiao() {
		cout << "加入奶和糖" << endl;
	};
};
//函数接口
void fun(father* p) {
	p->fun();
	delete p;
}
int main() {
	fun(new tea);
	cout << "----------" << endl;
	fun(new kafei);
	return 0;
}

五. 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:

将父类中的析构函数改为虚析构或者纯虚析构


虚析构和纯虚析构共性:
·可以解决父类指针释放子类对象·都需要有具体的函数实现


虚析构和纯虚析构区别:
·如果是纯虚析构,该类属于抽象类,无法实例化对象

代码: 

#include <iostream>
using namespace std;
class father {
public:
	//纯虚函数
	virtual void fun() =0;
	father() {
		cout << "father构造函数" << endl;
	}
	~father() {
		cout << "father析构函数" << endl;
	}
};

class son :public father {
public:
	//堆区开辟数据
	son(int age) {
		cout << "son构造函数" << endl;
		this->age = new int(age);
	}

	~son() {
		cout << "son析构函数" << endl;
		if (this->age != NULL) {
			delete age;
			age = NULL;
		}
	}
	void fun() {
		cout << *age<< "son的fun函数调用" << endl;
	}
	int* age;
};
void fun() {
	
	father* p = new son(21);
	delete p;
}
int main() {
	fun();
	return 0;
}

如图,当发生多态时,基类并不会调用子类的析构函数,当子类中含有堆区开辟的空间时。会造成内存泄漏。此时需要虚析构或纯虚析构来解决。

虚析构代码: 

#include <iostream>
using namespace std;
class father {
public:
	//纯虚函数
	virtual void fun() =0;
	father() {
		cout << "father构造函数" << endl;
	}
	virtual ~father() {
		cout << "father析构函数" << endl;
	}
};

class son :public father {
public:
	//堆区开辟数据
	son(int age) {
		cout << "son构造函数" << endl;
		this->age = new int(age);
	}

	~son() {
		cout << "son析构函数" << endl;
		if (this->age != NULL) {
			delete age;
			age = NULL;
		}
	}
	void fun() {
		cout << *age<< "son的fun函数调用" << endl;
	}
	int* age;
};
void fun() {
	
	father* p = new son(21);
	delete p;
}
int main() {
	fun();
	return 0;
}

纯虚析构

#include <iostream>
using namespace std;
class father {
public:
	//纯虚函数
	virtual void fun() =0;
	father() {
		cout << "father构造函数" << endl;
	}
	virtual ~father() = 0;
};
//纯虚函数必须
father::~father()
{
	cout << "father析构函数" << endl;
}

class son :public father {
public:
	//堆区开辟数据
	son(int age) {
		cout << "son构造函数" << endl;
		this->age = new int(age);
	}

	~son() {
		cout << "son析构函数" << endl;
		if (this->age != NULL) {
			delete age;
			age = NULL;
		}
	}
	void fun() {
		cout << *age<< "son的fun函数调用" << endl;
	}
	int* age;
};
void fun() {
	
	father* p = new son(21);
	delete p;
}
int main() {
	fun();
	return 0;
}

案例计算机

#include <iostream>
using namespace std;

class CPU {
public:
	//纯虚函数
	virtual void func() = 0;
};

class  Memory_Module {
public:
	//纯虚函数
	virtual void func() = 0;
};

class  Graphics_card {
public:
	//纯虚函数
	virtual void func() = 0;
};


class CPU_intel : public CPU {
public:
	void func() {
		cout << "intel的CPU工作" << endl;
	}
};

class Graphics_card_intel : public Graphics_card {
public:
	void func() {
		cout << "intel的显卡工作" << endl;
	}
};



class Memory_Module_intel : public Memory_Module {
public:
	void func() {
		cout << "intel的内存条工作" << endl;
	}
};



class CPU_lenovo: public CPU {
public:
	void func() {
		cout << "联想的CPU工作" << endl;
	}
};

class Graphics_card_lenovo : public Graphics_card {
public:
	void func() {
		cout << "联想的显卡工作" << endl;
	}
};
class Memory_Module_lenovo : public Memory_Module {
public:
	void func() {
		cout << "联想的内存条工作" << endl;
	}
};
class computer {
public:
	//当传入的是子类时发生多态
	computer() {};
	computer(CPU* CPU , Memory_Module* m, Graphics_card* g) {
		this->cpu = CPU;
		this->m = m;
		this->g = g;
	}
	void work() {
		cpu->func();
		m->func();
		g->func();
	}
private:
	CPU* cpu;
	Memory_Module* m;
	Graphics_card* g;
};

void fun() {
	CPU_lenovo* c1 = new CPU_lenovo;
	CPU_intel* c2 = new CPU_intel;
	Graphics_card_intel* g1 = new Graphics_card_intel;
	Graphics_card_lenovo* g2 = new Graphics_card_lenovo;
	Memory_Module_intel* m1 = new Memory_Module_intel;
	Memory_Module_lenovo* m2 = new Memory_Module_lenovo;

	cout << "第一台电脑" << endl;
	computer* com = new computer(c1,m1,g1);
	com->work();
	cout << "********************************" << endl;
	cout << "第二台电脑" << endl;
	computer* com1 = new computer(c2, m2, g2);
	
	com1->work();
}
int main() {
	fun();
	return 0;
}

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

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

相关文章

浅谈C++|类的继承篇

引子&#xff1a; 继承是面向对象三大特性之一、有些类与类之间存在特殊的关系&#xff0c;例如下图中: 我们发现&#xff0c;定义这些类时&#xff0c;下级别的成员除了拥有上一级的共性&#xff0c;还有自己的特性。 这个时候我们就可以考虑利用继承的技术&#xff0c;减少…

Learn Prompt-人工智能基础

什么是人工智能&#xff1f;很多人能举出很多例子说这就是人工智能&#xff0c;但是让我们给它定义一个概念大家又觉得很难描述的清楚。实际上&#xff0c;人工智能并不是计算机科学领域专属的概念&#xff0c;在其他学科包括神经科学、心理学、哲学等也有人工智能的概念以及相…

机器学习第六课--朴素贝叶斯

朴素贝叶斯广泛地应用在文本分类任务中&#xff0c;其中最为经典的场景为垃圾文本分类(如垃圾邮件分类:给定一个邮件&#xff0c;把它自动分类为垃圾或者正常邮件)。这个任务本身是属于文本分析任务&#xff0c;因为对应的数据均为文本类型&#xff0c;所以对于此类任务我们首先…

Jprofiler的使用查看oom

一、安装 idea安装插件 安装客户端 链接 IDEA配置Jprofiler执行文件 二、产生oom import java.util.ArrayList; import java.util.List;//测试代码 public class TestHeap {public static void main(String[] args) {int num 0;List<Heap> list new ArrayList&l…

【深度学习实验】线性模型(一):使用NumPy实现简单线性模型:搭建、构造损失函数、计算损失值

目录 一、实验介绍 二、实验环境 三、实验内容 0. 导入库 1. linear_model函数 2. loss_function函数 3. 定义数据 4. 调用函数 一、实验介绍 使用Numpy实现 线性模型搭建构造损失函数进行模型前向传播并计算损失值 二、实验环境 conda create -n DL python3.7 cond…

Learn Prompt-什么是ChatGPT?

ChatGPT&#xff08;生成式预训练变换器&#xff09;是由 OpenAI 在2022年11月推出的聊天机器人。它建立在 OpenAI 的 GPT-3.5 大型语言模型之上&#xff0c;并采用了监督学习和强化学习技术进行了微调。 ChatGPT 是一种聊天机器人&#xff0c;允许用户与基于计算机的代理进行对…

LVS+Haproxy

LVSHaproxy 一、Haproxy简介1.1、Haproxy应用分析1.2、Haproxy的特性1.3、常见负载均衡策略1.4、LVS、Haproxy、Nginx区别1.5、 Haproxy的优点1.6、常见的Web集群调度器 二、Haproxy部署实例四、日志定义优化 一、Haproxy简介 Haproxy 是一个使用C语言编写的自由及开放源代码软…

ES6中新增加的Proxy对象及其使用方式

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ Proxy对象的基本概念Proxy对象的主要陷阱&#xff08;Traps&#xff09; ⭐ 使用Proxy对象⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来…

Hugging Face使用Stable diffusion Diffusers Transformers Accelerate Pipelines

Diffusers A library that offers an implementation of various diffusion models, including text-to-image models. 提供不同扩散模型的实现的库&#xff0c;代码上最简洁&#xff0c;国内的问题是 huggingface 需要翻墙。 Transformers A Hugging Face library that pr…

log4j2漏洞复现

log4j2漏洞复现 漏洞原理 log4j2框架下的lookup查询服务提供了{}字段解析功能&#xff0c;传进去的值会被直接解析。例如${sys:java.version}会被替换为对应的java版本。这样如果不对lookup的出栈进行限制&#xff0c;就有可能让查询指向任何服务&#xff08;可能是攻击者部署…

JavaScript-箭头函数

es6的箭头函数具体使用 es6之后提出了箭头函数 更加简洁方便 注意 &#xff1a; 特点:只有一个形参可以省略括号 大括号是否可以省略&#xff1f; 是 只有一句代码的时候可以省略 具体看代码演示&#xff1a; 代码 <!DOCTYPE html> <html lang"en"&…

Python 图形化界面基础篇:处理鼠标事件

Python 图形化界面基础篇&#xff1a;处理鼠标事件 引言 Tkinter 库简介步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a;创建一个 Canvas 画布步骤4&#xff1a;处理鼠标事件步骤5&#xff1a;启动 Tkinter 主事件循环 完整示例代码代…

解决中国科大 USTC 邮箱系统的超大附件上传的邮箱控件安装问题

USTC邮箱系统上传超过 48M 的附件的步骤&#xff1a; 从文件中转站上传文件&#xff0c;会提示下载邮箱控件 cmplugin_setup.exe &#xff0c;默认安装C盘即可 2. 安装好之后依然无法上传超大文件&#xff0c;因为只有 IE 浏览器支持该功能&#xff0c;所以可以使用 Edge 浏览…

timer trigger function

创建&#xff08;使用vscode&#xff09; 选择Timer trigger 命名 设置多久触发一次&#xff08;该语句是5分钟一次&#xff09; 创建完成 在下面直接编辑想要运行的代码。

【计算机网络】75 张图详解:网络设备、网络地址规划、静态路由(万字长文)

75 张图详解&#xff1a;网络设备、网络地址规划、静态路由 1.网络设备1.1 交换机1.2 路由器 2.网络地址规划2.1 IP 地址2.2 分类地址2.3 子网掩码2.4 无类地址2.5 子网划分2.5.1 示例一2.5.2 示例二 2.6 超网合并 3.静态路由3.1 路由表3.2 直连路由3.3 静态路由3.4 默认路由3.…

OpenCV之怀旧色、冰冻滤镜、熔铸滤镜

怀旧色 源码&#xff1a; void huaijiu(Mat& src,Mat& dst) {for (int h 0;h < src.rows;h ){uchar *d1 src.ptr<uchar>(h);uchar *d2 dst.ptr<uchar>(h);for (int w 0;w < src.cols;w ){int w3 3*w;int r d1[w3 2];int g d1[w3 1];int …

这种长海报制作技巧大揭秘,让你的作品与众不同

制作吸引人的长图海报&#xff0c;是许多人在社交媒体、广告宣传、活动策划等场合中经常需要面对的任务。然而&#xff0c;对于不熟悉设计软件的人来说&#xff0c;这可能是一个挑战。幸运的是&#xff0c;现在有许多在线工具和模板可以帮助我们解决这个问题。下面&#xff0c;…

索引-动图演示存储过程

索引 二叉树存储过程演示 BThree存储过程 sql二级索引搜索过程 Id是唯一键&#xff0c;聚集索引 只存在一个 Name是二级索引 可以存在多个 第一种效率更高&#xff0c;不需要回表

C语言经典100例题(56-60)--画圆;画方;画线

目录 【程序56】题目&#xff1a;画图&#xff0c;学用circle画圆形 【程序57】题目&#xff1a;画图&#xff0c;学用line画直线。 【程序58】题目&#xff1a;画图&#xff0c;学用rectangle画方形。 【程序59】题目&#xff1a;画图&#xff0c;综合例子。 【程序60】题…

idea移除许可证

目录 一、介绍 二、操作步骤 一、介绍 当自己的idea日期要到了&#xff0c;又想续上&#xff0c;但是覆盖不了之前的日期&#xff0c;新的没办法生效。那么就要把原先的许可证先移除&#xff0c;再重新续上新的。 二、操作步骤 1.点击idea的右上角的这个展开 2.选择帮助…