C++多态

2023-11-05

多态是C++面向对象三大特性之一

多态分为两类

静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态: 派生类和虚函数实现运行时多态

静态多态和动态多态区别:

静态多态的函数地址早绑定  -  编译阶段确定函数地址
动态多态的函数地址晚绑定  -  运行阶段确定函数地址

下面这段代码运行结果是“动物在说话”,原因是在编译阶段确定了函数地址,也就是地址早绑定

#include<iostream>
using namespace std;
//多态
//动物类
class Animal
{
public:
	void speak()
	{
		cout << "动物在说话" << endl;
	}


};
//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
void doSpeak(Animal &animal)//Animal &引用 animal = cat;
{
	animal.speak();
}

//猫类
class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
};

void test01()
{
	Cat cat;
	doSpeak(cat);
} 
int main() {
	test01();
	system("pause");
	return 0;
}

 

给这段代码稍作更改,用虚函数,改为动态多态

class Animal
{
public:
	//虚函数
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}


};

现在再运行程序就是“小猫在说话”了

 动态多态满足条件

        1.有继承关系

        2.子类需要重写父类中的虚函数

动态多态的使用

        父类的指针或者引用执行子类对象

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

案例1:计算器

普通写法写计算器

#include<iostream>
using namespace std;
#include<string>
//分别利用普通写法和多态技术实现计算器
//普通写法
class Calculator
{
public:
	int getResult(string oper)
	{
		if (oper == "+")
		{
			return m_Num1 + m_Num2;
		}
		else if (oper == "-")
		{
			return m_Num1 - m_Num2;
		}
		else if (oper == "*")
		{
			return m_Num1 * m_Num2;
		}
		//如果想扩展新的功能,需要修改源码
		//在真实的开发中 提倡 开闭原则
		//开闭原则:对扩展进行开放 对修改进行关闭
	}
	int m_Num1;//操作数1
	int m_Num2;//操作数2
};

void test01()
{
	//创建计算器对象
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;
	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
	cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
	cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
}

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

在真实的开发中 提倡 开闭原则
开闭原则:对扩展进行开放 对修改进行关闭

用多态来写计算器

#include<iostream>
using namespace std;
#include<string>

//利用多态实现计算器
//实现计算器抽象类

class AbstractCalculator
{
public:


	virtual int getResult()
	{
		return 0;
	}
	int m_Num1;
	int m_Num2;
};
//加法计算器类
class AddCalculator : public AbstractCalculator
{
public:
	int getResult()
	{
		return m_Num1+m_Num2;
	}

};
//减法
class SubCalculator : public AbstractCalculator
{
public:
	int getResult()
	{
		return m_Num1 - m_Num2;
	}

};
//乘法
class MulCalculator : public AbstractCalculator
{
public:
	int getResult()
	{
		return m_Num1 * m_Num2;
	}

};
void test02()
{
	//多态使用条件
	//父类指针或者引用指向子类对象
	//加法运算
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;
	//减法运算
	abc = new SubCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
	//乘法运算
	abc = new MulCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;
	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
}
int main()
{
	test02();
	system("pause");
	return 0;
}

  

使用多态的好处
1.组织结构清晰
2.可读性强
3.对于前期和后期的代码 维护性高

虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数

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

当类中有了纯虚函数,这个类也称为==抽象类==

抽象类特点:

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

#include<iostream>
using namespace std;

//纯虚函数和抽象类
class Base
{
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	//抽象类特点:
	//1.无法实例化对象
	//2.抽象类的子类 必须要重写父类中的纯虚函数,否则也属于抽象类
	virtual void func() = 0;

};

class Son : public Base
{
public:
	virtual void func() {
		cout << "func函数调用" << endl;
	}
};

void test01()
{
	//Base b;//抽象类无法实例化对象
	//new Base;//抽象类无法实例化对象
	//Son s;//子类必须重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();
}
int main()
{
	test01();
	system("pause");
	return 0;
}

案例2:制作饮品

#include<iostream>
using namespace std;

//多态案例2 制作饮品
class AbstractDrinking
{
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//倒入杯中
	virtual void PourInCup() = 0;
	//加入佐料
	virtual void PutSomething() = 0;
	//制作饮品
	void makeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};
//制作咖啡
class Coffee : public AbstractDrinking
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "煮水" << endl;
	}
	//冲泡
	virtual void Brew()
	{
		cout << "冲泡咖啡" << endl;
	}
	//倒入杯中
	virtual void PourInCup()
	{
		cout << "倒入杯中" << endl;
	}
	//加入佐料
	virtual void PutSomething()
	{
		cout << "加入糖和牛奶" << endl;
	}
};


//制作茶叶
class Tea : public AbstractDrinking
{
public:
	//煮水
	virtual void Boil()
	{
		cout << "煮水" << endl;
	}
	//冲泡
	virtual void Brew()
	{
		cout << "冲泡茶叶" << endl;
	}
	//倒入杯中
	virtual void PourInCup()
	{
		cout << "倒入杯中" << endl;
	}
	//加入佐料
	virtual void PutSomething()
	{
		cout << "加入柠檬" << endl;
	}
};
//制作函数
void doWork(AbstractDrinking* abs)//AbstractDrinking * abs = new Coffee
{
	abs->makeDrink();
	delete abs;//释放
}
void test01()
{
	//制作咖啡
	doWork(new Coffee);
	//制作茶叶
	doWork(new Tea);
}
int main()
{
	test01();
	system("pause");
		return 0;
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++多态 的相关文章

  • Qt - QProcess 不工作

    我尝试启动 Internet Explorer 所以我使用下面的代码 QProcess process new QProcess this QString temp C Program Files Internet Explorer iex
  • Exit() 时是否调用基本对象析构函数?

    我意识到这个问题已经出现过几次 但我试图获得上述问题的明确答案 但我不断遇到相互矛盾的信息 我需要知道的是 当我使用 exit 时 基本类对象是否被破坏 我知道需要删除动态内存 但我的意思更像是 include
  • 将类对象放置在向量中?

    我注意到我可以将一个类放置在一个向量中 这是我的程序 我收到以下错误 out blackjack exe blackjack obj blackjack obj error LNK2019 unresolved external symbo
  • 按扩展名过滤搜索文件返回太多结果

    我正在开发一个 C 控制台应用程序 它必须管理 Windows 操作系统上的文件 我需要获取具有特定扩展名的文件名 列表 我找到了很多解决方案 最建议的是以下一种 HANDLE hFind WIN32 FIND DATA data hFin
  • 语音识别编程问题入门

    所以 你们可能都看过 钢铁侠 其中托尼与一个名为贾维斯的人工智能系统进行交互 演示剪辑here http www youtube com watch v Go8zsh1Ev6Y 抱歉 这是广告 我非常熟悉 C C 和 Visual Basi
  • Eigen 和 OpenMP:由于错误共享和线程开销而没有并行化

    系统规格 Intel Xeon E7 v3 处理器 4 插槽 16 核 插槽 2 线程 核心 Eigen 系列和 C 的使用 以下是代码片段的串行实现 Eigen VectorXd get Row const int j const int
  • 是否使用 C# 数据集? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我对 C 中的数据集概念有点困惑 编码 ASP NET 站点 但这并不重要 在我的阅读中 我了解到它们 本质上 用作我的应用程序和我的
  • 不可变类与结构

    以下是类与 C 中的结构的唯一区别 如果我错了 请纠正我 类变量是引用 而结构变量是值 因此在赋值和参数传递中复制结构的整个值 类变量是存储在堆栈上的指针 指向堆上的内存 而结构变量作为值存储在堆上 假设我有一个不可变的结构 该结构的字段一
  • 在 C 中使用枚举而不是 #defines 作为编译时常量是否合理?

    在 C 工作了一段时间后 我将回到 C 开发领域 我已经意识到 在不必要的时候应该避免使用宏 以便让编译器在编译时为您做更多的工作 因此 对于常量值 在 C 中我将使用静态 const 变量或 C 11 枚举类来实现良好的作用域 在 C 中
  • 将函数参数类型提取为参数包

    这是一个后续问题 解包 元组以调用匹配的函数指针 https stackoverflow com questions 7858817 unpacking a tuple to call a matching function pointer
  • C++ 对象用 new 创建,用 free() 销毁;这有多糟糕?

    我正在修改一个相对较大的 C 程序 不幸的是 并不总是清楚我之前的人使用的是 C 还是 C 语法 这是在一所大学的电气工程系 我们 EE 总是想用 C 来做所有事情 不幸的是 在这种情况下 人们实际上可以逃脱惩罚 但是 如果有人创建一个对象
  • 使动态创建的链接标签在 Winforms 中可点击

    我正在制作一个程序 允许用户单击由动态链接标签创建的公司名称 在我想知道如何做到这一点之前 我从未在 C 中使用过链接标签 可为特定用户生成的业务数量各不相同 因此每个用户的链接标签数量并不相同 然后我想捕获业务 ID 以进行 Json 调
  • Visual Studio 2015:v120 与 v140?

    仅供参考 Win10 x64 我今天开始尝试 Visual Studio 2015 在弄清楚如何运行 C C 部分后 我尝试加载一个大型个人项目 该项目使用非官方的glsdk http glsdk sourceforge net docs
  • 代码中的.net Access Forms身份验证“超时”值

    我正在向我的应用程序添加注销过期警报 并希望从我的代码访问我的 web config 表单身份验证 超时 值 我有什么办法可以做到这一点吗 我认为您可以从 FormsAuthentication 静态类方法中读取它 这比直接读取 web c
  • Visual Studio 2015 - Web 项目上缺少共享项目参考选项卡

    我从 MSDN 订阅升级到 Visual Studio 2015 因为我非常兴奋地阅读有关共享项目的信息 当我们想要做的只是重用代码时 不再需要在依赖项中管理 21382 个 nuget 包 所以我构建了一个测试共享项目 其中包含一些代码
  • C++:二叉树所有节点值的总和

    我正在准备面试 我被一个二叉树问题困住了 我们如何计算二叉树所有节点中存在的值的总和 优雅的递归解决方案 伪代码 def sum node if node NULL return 0 return node gt value sum nod
  • 了解 Lambda 表达式和委托 [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我已经尝试解决这个问题很长一段时间了 阅读在线博客和文章 但到目前为止还没有成功 什么是代表 什么是 Lambda 表达式 两者的优点
  • EntityFramework 6.0.0.0 读取数据,但不插入

    我创建了一个基于服务的数据库 folderName gt Add New Item gt Data gt Service based Database文件到 WPF 应用程序中 然后我用过Database First方法并创建了Person
  • 在 System.Type 上使用条件断点时出错

    这是函数 public void Init System Type Type this Type Type BuildFieldAttributes BuildDataColumns FieldAttributes 我在第一行设置了一个断点
  • MySqlConnectionStringBuilder - 使用证书连接

    我正在尝试连接到 Google Cloud Sql 这是一个 MySql 解决方案 我能够使用 MySql Workbench 进行连接 我如何使用 C 连接MySqlConnectionStringBuilder 我找不到提供这三个证书的

随机推荐

  • vue自定义穿梭框支持远程滚动加载

    欢迎点击领取 前端面试题进阶指南 前端登顶之巅 最全面的前端知识点梳理总结 分享一个使用比较久的 技术框架公司的选型 老项目 vue2 iview ui 方案的实现思路是共性的 展现UI样式需要你们自定义进行更改 因为iview是全局注入
  • WSL 配置中文输入法

    1 安装输入法 sudo apt install dbus x11 im config fonts noto fcitx fcitx pinyin fcitx sunpinyin fitx googlepinyin 2 设置自动启动 命令行
  • 深入解析G1垃圾收集器与性能优化

    本文详细介绍G1垃圾收集器的参数配置 如何进行性能调优 以及怎样对GC性能进行分析和评估 文章目录 0 G1简介 1 垃圾回收阶段简介 2 纯年轻代模式的垃圾收集 3 混合模式的垃圾收集 4 标记周期的各个阶段 5 常用参数与默认值 XX
  • 整合Windows 7 / server 2008更新补丁包

    哈喽 大家好 今天主要是给学习win7和server2008的小伙伴准备的一样好东西 UpdatePack7R2 这是是由俄罗斯大神Simplix制作的Windows 7 和 server 2008的更新补丁离线安装包 里面整合了Windo
  • 数组中子数组和为固定值的题目汇总

    开头附件一部分数组去重的知识 C 中数组 Vector中去除重复元素 unique函数是一个去重函数 去除相邻中的重复元素 只留一个 其中 最关键的是 并不是删除并不是把重复的元素删除 而是全部放倒数组的后面 因为 unique只是去除 相
  • linux查询端口冲突情况

    1 netstat anp grep 80 输出 unix 2 ACC STREAM LISTENING 48026 7112 master private tlsmgr 若 出现LISTEN则冲突 LISTENING正常
  • 01-----关于更换网络之后,xshell无法连接虚拟机(之前还能连接)

    1 解析 我本来在一个网络中是正常能使用xshell连接我的本地虚拟机的 但是后面回到家里的wifi后 突然就连不上了 经过排查 发现是新的wifi的与原本建立好的网络环境不一致 具体是ip不在同一网段 重新配置一下 例如我是这样操作的 看
  • Visual studio Code的clangd插件一篇通,上乘的C/C++开发环境配置

    系列文章 Visual studio Code的C C 开发环境搭建 Visual studio Code的clangd插件一篇通 上乘的C C 开发环境配置 苦于微软出品的 C C 插件久矣 时不时就会出现代码提示失效 跳转失败的问题 现
  • 算法实现之二叉树的前序、中序、后序递归和非递归遍历以及层序遍历

    概述 二叉树是每个节点最多有两个子树的树结构 通常子树被称作左子树和右子树 二叉树的遍历通常分为 前序遍历 中序遍历 后序遍历和层序遍历 前序遍历 前序递归遍历算法 访问根结点 gt 递归遍历根结点的左子树 gt 递归遍历根结点的右子树 中
  • linux tr命令完整,linux tr命令详解

    tr命令可以对来自标准输入的字符进行替换 压缩和删除 下面由学习啦小编为大家整理了linux tr命令的相关知识 希望对大家有帮助 linux tr命令用途 tr命令可以对来自标准输入的字符进行替换 压缩和删除 它可以将一组字符变成另一组字
  • 大数据分析——云南的花样之旅

    认真阅读过小伙伴们的评论 最终将我们此行的目的地定在了山清水秀的云南了 对于从小生活在北方的小编 对云南还是抱着不小的憧憬呢 下面就来看看数据人员给出的官方数据吧 首先我们就来说说久负盛名的云南大理吧 先看一下数据 看了下数据结果 小编才知
  • 【好工具】不需要知道地址,一个命令就能下载任何文件,它不香吗?

    今天介绍一款浏览器插件 可以直接将文件的下载链接直接转换成 Linux 命令 方便无操作界面的电脑下载无直链的文件 作者 编辑 Leong 导言 使用过 Linux 的朋友可能知道 大部分软件都是不支持 Linux 系统的 Windows系
  • List及String类的Contains方法底层源码解析

    public boolean Contains Object o 此处的o即为contains方法中的参数对象 return indexOf o gt 0 如果数值 gt 0 则返回true public int indexOf Objec
  • mysql中的Date日期格式的问题:只有日期没有时间及格式化时间

    只有日期没有时间 把xml中的date改为timestamp 格式化最简单的方法 JsonFormat pattern yyyy MM dd HH mm ss timezone GMT 8 转载于 https www cnblogs com
  • day7pyecharts制图-组合图

    Pyecharts制图 标题一 柱状图 导入柱状图的类 from pyecharts charts import Bar 导入图标配置类 from pyecharts import options 基本的柱状图 1 创建图标对象 bar B
  • 在Linux上部署KVM和使用

    实验前 我们关闭防火墙 systemctl stop firewalld systemctl enable firewalld 如果我们之前安装过KVM或者有残余 可以先执行以下命令来清除一下 yum remove rpm qa egrep
  • oracle中常用的set命令

    time 2008 2 1author skate oracle中常用的set命令 前一阵在做数据库数据的转移 所以就写个shell脚本来自动化完成 用shell脚本生成sql脚本时 就要用到oracle的set的命令来去掉一些不需要的提示
  • JVM内存划分

    1 程序计数器 每一个线程都有自己的一个程序计数器 创建线程时被一起创建 用于记录当前字节码执行位置 例如一个线程在执行过程中被挂起 一定时间片后 又继续执行 此时需要进行现场恢复 那么就可以在对应的程序记录器中找到被挂起时的下一条指令将要
  • java中mybatis原理,mybatis的工作原理

    由以上图可以看出 mybatis框架在操作数据库时大致经过了8个步骤 对这8个步骤分析如下 1 读取mybatis配置文件mybatis config xml mybatis config xml配置文件作为mybatis的全局配置文件 配
  • C++多态

    多态是C 面向对象三大特性之一 多态分为两类 静态多态 函数重载 和 运算符重载属于静态多态 复用函数名 动态多态 派生类和虚函数实现运行时多态 静态多态和动态多态区别 静态多态的函数地址早绑定 编译阶段确定函数地址 动态多态的函数地址晚绑