当前位置: 首页 > news >正文

c++学习——多态

多态

    • **多态的语法**
    • **多态的底层原理图**
    • **多态案1——计算机类**
    • **纯虚函数和抽象类**
    • **多态案例2——饮品**
    • **虚析构和纯虚析构**
    • **多态案例3—— 电脑组装**

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

C++中的多态性通常通过虚函数来实现。虚函数是在基类中声明的,派生类可以重写该函数并提供自己的实现。当通过基类指针或引用调用虚函数时,实际调用的是派生类的实现。这种机制允许在运行时动态地确定要调用哪个函数,从而实现多态性。如果没有虚函数,就无法实现多态性。

多态的语法

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
//多态//动物类
class Animal
{
public://虚函数virtual void speak(){cout << "动物在说话" << endl;}
};//猫类
class Cat :public Animal
{
public:void speak(){cout << "小猫在说话" << endl;}
};//狗类
class Dog :public Animal
{
public:void speak(){cout << "小狗在说话" << endl;}
};
//执行说话的函数
//c++中父类的引用能够直接指向子类对象
//地址早绑定  在编译阶段确定了函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定
//也就是地址晚绑定  //动态多态的满足条件
//1、有继承关系
//2、子类要重写父类的虚函数  //重载和重写的区别
//1、重载:函数的名字相同,参数不同
//2、重写:函数的返回值类型,函数名要相同,参数列表要完全相同//动态多态的使用
//父类的指针或者引用 指向子类对象
void doSpeak(Animal &animal)//Animal &animal=cat
{animal.speak();
}void test01()
{Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);
}int main()
{test01();system("pause");return EXIT_SUCCESS;
}

在这里插入图片描述

多态的底层原理图

动态的多态

在这里插入图片描述
子类不重写的结构原理图
在这里插入图片描述
静态成员函数和非静态成员函数都是存放在代码区的。
是类可以直接调用静态成员函数。不可以直接调用非静态成员函数
因此这里的Size(1)

子类重写后的结构原理图
在这里插入图片描述

多态案1——计算机类

案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类
多态的优点:
代码组织结构清晰 可读性强
利于前期和后期的扩展以及维护

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;//分别利用普通写法和多态技术实现计算机类//普通写法
class Calculator
{
public:int getResulr(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;int m_Num2;
};void test01()
{//创建计算机对象Calculator c;c.m_Num1 = 10;c.m_Num2 = 10;//cout << c.getResulr("+") << endl;cout << c.m_Num1 << " + " << c.m_Num2 << " = " <<c.getResulr("+")<< endl;cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResulr("-") << endl;cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResulr("*") << endl;
}//利用多态来实现计算器
//多态的好处:
//1、组织结构清晰
//2、可读性强
//3、对于前期和后期的拓展和维护性高
//实现计算机抽象类
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 AbstractCalculator;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 = 100;abc->m_Num2 = 100;cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;delete abc;//乘法运算abc = new MulCalculator;abc->m_Num1 = 100;abc->m_Num2 = 100;cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;delete abc;
}int main()
{//test01();test02();system("pause");return EXIT_SUCCESS;
}

纯虚函数和抽象类

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;//纯虚函数和抽象类
//只要有一个纯虚函数,这个类称为抽象类
class Base
{
public:virtual void func() = 0;
};class Son :public Base
{
public://子类必须重写父类中的纯虚函数,否则无法实例化//virtual void func() = 0;
};void test01()
{//1、无法实例化对象//Base b;   err//new Base;   err//2、子类中没有重写父类中的纯虚函数//Son s;      err
}int main()
{//test01();system("pause");return EXIT_SUCCESS;
}

多态案例2——饮品

案例描述:
制作饮品的大致流程为:煮水-冲泡-倒入杯中-加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#define _CRT_SECURE_NO_WARNINGS
#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
{//煮水virtual void Boil(){cout << "煮农夫山泉" << endl;}//冲泡virtual void Brew(){cout << "冲泡咖啡" << endl;}//倒入杯中virtual void PourInCup(){cout << "倒入杯中" << endl;}//加入辅料virtual void PutSomething(){cout << "加入精液" << endl;}
};//制作茶叶
class Tea :public AbstractDrinking
{//煮水virtual void Boil(){cout << "煮矿泉水" << endl;}//冲泡virtual void Brew(){cout << "冲泡茶叶" << endl;}//倒入杯中virtual void PourInCup(){cout << "倒入玻璃杯" << endl;}//加入辅料virtual void PutSomething(){cout << "加入尿液" << endl;}
};//制作函数
void doWork(AbstractDrinking * abs)
{abs->makeDrink();delete abs;
}int main()
{//制作咖啡doWork(new Coffee);cout << "----------------------------" << endl;;//制作茶叶doWork(new Tea);system("pause");return EXIT_SUCCESS;
}

在这里插入图片描述

虚析构和纯虚析构

这个案例是在子类中有属性开辟到了堆区,因此走子类中的析构代码,如果是多态是走不到的,因此需要加入虚析构或者是纯虚析构,而纯虚析构要有具体的函数实现,不然不能示例化对象,而虚析构是可以实例化对象的,

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;//虚析构和纯虚析构class Animal
{
public:Animal(){cout << "Animal的构造函数调用" << endl;}//虚析构//利用虚析构可以解决  父类指针释放子类对象时不干净的问题//virtual ~Animal()//{//	cout << "Animal的析构函数调用" << endl;//}//纯虚析构也能解决问题//无法解析的外部命令是在链接阶段出现的问题//纯虚析构需要有声明也需要实现//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象virtual ~Animal() = 0;//纯虚函数virtual void speak() = 0;
};//纯虚析构的代码实现
Animal::~Animal()
{cout << "纯虚析构函数调用" << endl;
}//猫类
class Cat :public Animal
{
public:Cat(string name){cout << "Cat调用了构造函数" << endl;m_Name = new string(name);}~Cat(){if (m_Name != NULL){cout << "Cat的析构函数调用" << endl;delete m_Name;m_Name = NULL;}}virtual void speak(){cout << *m_Name << "小猫在说话" << endl;}string  *m_Name;
};void test01()
{//这里是用父类的指针指向子类对象//父类的指针在析构时候,不会调用子类中的析构函数 //导致了子类中如果又堆区的属性  会出现内存泄露的情况//因此引入了虚析构Animal * animal = new Cat("Tom");animal->speak();delete animal;
}int main()
{test01();system("pause");return EXIT_SUCCESS;
}

在这里插入图片描述
总结:
1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3.拥有纯虚析构函数的类也属于抽象类

多态案例3—— 电脑组装

案例描述:
电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作

#include<iostream>
using namespace std;
#include<string>//抽象不同零件类
//抽象计算函数
class CPU
{
public:virtual void calculate() = 0;
};
//抽象显示函数
class VideoCard
{
public:virtual void display() = 0;
};
//抽象存储函数
class Memory
{
public:virtual void storage() = 0;
};//电脑类
class Computer
{
public://构造函数中传入三个零件指针Computer(CPU * cpu, VideoCard * vc, Memory * mem){m_cpu = cpu;//指针接收m_vc = vc;m_mem = mem;}//提供工作的函数void work(){//调用每个零件工作的接口m_cpu->calculate();m_vc->display();m_mem->storage();}//提供析构函数,释放 3个电脑零件~Computer(){if (m_cpu != NULL){delete m_cpu;m_cpu = NULL;}if (m_vc != NULL){delete m_vc;m_vc = NULL;}if (m_mem != NULL){delete m_mem;m_mem = NULL;}}
private:CPU * m_cpu;//CPU的零件指针VideoCard * m_vc;//显卡零件指针Memory * m_mem;//内存零件指针};//具体厂商
//Intel厂商
class IntelCpu :public CPU
{
public:virtual void calculate(){cout << "Intel的CPU开始计算了!" << endl;}
};class IntelVideoCard :public VideoCard
{
public:virtual void display(){cout << "Intel的VideoCard开始显示了!" << endl;}
};class IntelMemory :public Memory
{
public:virtual void storage(){cout << "Intel的Memory开始存储了!" << endl;}
};//Lenovo厂商
class LenovoCpu :public CPU
{
public:virtual void calculate(){cout << "Lenovo的CPU开始计算了!" << endl;}
};class LenovoVideoCard :public VideoCard
{
public:virtual void display(){cout << "Lenovo的VideoCard开始显示了!" << endl;}
};class LenovoMemory :public Memory
{
public:virtual void storage(){cout << "Lenovo的Memory开始存储了!" << endl;}
};void test01()
{//第一台电脑CPU * intelCpu = new IntelCpu;//父类指向子类对象,//需释放VideoCard * intelVCard = new IntelVideoCard;//需释放Memory * intelMem = new IntelMemory;//需释放,//1.直接delete 在 delete computer1; 后//2.提供析构函数在 class Computer  中//创建第一台电脑Computer * computer1 = new Computer(intelCpu, intelVCard, intelMem);//需释放computer1->work();delete computer1;cout << "-----------------------------" << endl;cout << "第二台电脑开始工作" << endl;//创建第二台电脑Computer * computer2 = new Computer(new LenovoCpu, new LenovoVideoCard, new LenovoMemory);//需释放computer2->work();delete computer2;cout << "-----------------------------" << endl;cout << "第三台电脑开始工作" << endl;//创建第三台电脑Computer * computer3 = new Computer(new LenovoCpu, new IntelVideoCard, new LenovoMemory);//需释放computer3->work();delete computer3;
}int main()
{test01();system("pause");return 0;
}

在这里插入图片描述

http://www.lryc.cn/news/92231.html

相关文章:

  • Java SPI机制及原理详解
  • 不压缩打包layui
  • 过去、现在及未来
  • leetcode701. 二叉搜索树中的插入操作(java)
  • Docker的容器管理操作
  • 计算机组成原理——中央处理器
  • tidb变更大小写敏感问题的总结
  • 法规标准-UN R158标准解读
  • 160个CrackMe之002
  • 3. 响应状态码及Response对象的status_code属性
  • MIME 类型列表 03
  • SpringBoot项目登录并接入MFA二次认证
  • 算法与数据结构(三)
  • 亚马逊云科技出海日,让数字经济出海扩展到更多行业和领域
  • Pb协议的接口测试
  • 2. 分布式文件系统 HDFS
  • 借助金融科技差异化发展,不一样的“破茧”手法
  • typescript中type、interface的区别
  • Ingress详解
  • 【递归算法的Java实现及其应用】
  • 2023年度第四届全国大学生算法设计与编程挑战赛(春季赛)
  • 如何用PHP获取各大电商平台的数据
  • 一站式完成车牌识别任务:从模型优化到端侧部署
  • Linux4.8Nginx Rewrite
  • DHT11温湿度传感器
  • RestTemplate超简单上手
  • 监控系统设计原则及实现目标
  • VulnHub项目:MONEYHEIST: CATCH US IF YOU CAN
  • 对象存储OSS简介,一分钟了解对象存储OSS
  • SpringCloud_微服务基础day2(Eureka简介与依赖导入,服务注册与发现)