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

黑马程序员C++核心编程笔记--4 类和对象--多态

1.多态的基本概念

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

多态分为两类

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

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

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

下面通过案例进行讲解多态

#include <iostream>
using namespace std;class Animal
{
public:// Speak函数就是虚函数// 在函数前加上 virtual,表示这是一个“虚函数”,编译器不会马上决定调用哪个版本的函数,// 而是等到程序运行时,根据对象真实的类型来决定调用哪个类的函数(运行时多态)。virtual void Speak(){cout << "动物在说话" << endl;}
};class Cat: public Animal
{
public:void Speak(){cout << "小猫在说话" << endl;}
};class Dog:public Animal
{
public:void Speak(){cout << "小狗在说话" << endl;}
};// 我们希望传入什么对象就调用什么对象的函数
// 如果函数地址在编译阶段就能确定,那么静态联编
// 如果函数地址在运行阶段才能确定,那么动态联编
void DoSpeak(Animal& animal)
{animal.Speak();
}// 多态满足条件:
// 1.有继承关系
// 2.子类重写父类中的虚函数
// 多态使用:
// 父类指针或引用指向子类对象void test()
{Cat cat;DoSpeak(cat);Dog dog;DoSpeak(dog);
}int main()
{test();return 0;
}

总结:

多态满足条件

  • 有继承关系

  • 子类重写父类中的虚函数

  • 多态使用条件

  • 父类指针或引用指向子类对象

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

2.多态案例一:计算器类

示例:

#include <iostream>
using namespace std;// 普通实现
class Calculator
{
public:int m_Num1;int m_Num2;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;}// 如果要提供新的运算,需要修改源码}};// 普通实现测试
void test1()
{Calculator c;c.m_Num1 = 10;c.m_Num2 = 20;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;
}//多态实现
// 抽象计算器
// 多态优点:代码组织结构清晰,可读性强,利于前期和后期扩展以及维护
class AbstractCalculator
{
public:int m_Num1;int m_Num2;virtual int getResult(){return 0;}
};// 加法计算器
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 test2()
{// 创建加法计算器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() {//test01();test2();return 0;
}

总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

3.虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容,因此可以将虚函数改为纯虚函数

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

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

抽象类特点:

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

示例:

#include <iostream>
using namespace std;class Base
{
public:// 纯虚函数// 类中只要有一个纯虚函数就称为抽象类// 抽象类无法实体化对象// 子类必须重写父类中的纯虚函数,否则也属于抽象类virtual void show() = 0;
};class Son : public Base
{
public:virtual void show(){cout << "show调用" << endl;}
};void test1()
{Base* base = NULL;// base = *new Base;    // 错误,抽象类无法实例化对象base = new Son;base->show();delete base;    // 使用完后要销毁
}int main()
{test1();return 0;
}

4.多态案例二:制作饮品

制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

示例:

#include <iostream>
using namespace std;/*
* 制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料
* 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*/// 抽象制作饮品
class AbstractDrinkind
{
public:// 烧水virtual void Boil() = 0;// 冲泡virtual void Brew() = 0;// 倒入杯中virtual void PourInCup() = 0;// 加入辅料virtual void AddCondiments() = 0;// 规定流程void MakeDrink(){Boil();Brew();PourInCup();AddCondiments();}
};// 制作咖啡
class Coffee: public AbstractDrinkind
{
public://  烧水void Boil(){cout << "把水煮沸" << endl;}// 冲泡咖啡void Brew(){cout  << "用沸水冲泡咖啡" << endl;}// 倒入杯中void PourInCup(){cout  << "把咖啡倒进杯子" << endl;}// 加入辅料void AddCondiments(){cout  << "加糖和牛奶" << endl;}
};// 制作茶水
class Tea : public AbstractDrinkind
{
public://  烧水void Boil(){cout << "把水煮沸" << endl;}// 冲泡茶void Brew(){cout  << "用沸水浸泡茶叶" << endl;}// 倒入杯中void PourInCup(){cout << "把茶倒进杯子" << endl;}// 加入辅料void AddCondiments(){cout  << "加枸杞" << endl;}
};// 业务函数
void Dowork(AbstractDrinkind* drink)
{drink -> MakeDrink();delete drink;
}void test()
{Dowork(new Coffee);cout << "-----------------"  << endl;Dowork(new Tea);
}int main()
{test();return 0;
}

5.虚析构和纯虚析构

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

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

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

虚析构语法:
virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0;

类名::~类名(){}

#include <iostream>
using namespace std;class Animal
{
public:Animal(){cout << "Animal()" << endl;}virtual void Speak() = 0;// 析构函数加上virtual关键字,变成虚析构函数// virtual ~Animal()// {//     cout << "~Animal()" << endl;// }virtual ~Animal() = 0;
};Animal::~Animal()
{cout << "~Animal()" << endl;
}// 和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化class Cat:public Animal
{
public:string * m_name;Cat(string name){cout << "Cat构造函数调用" << endl;m_name = new string(name);}virtual void Speak(){cout <<  "m_name: " << *m_name << ": 喵喵喵" << endl;}~ Cat(){cout << "~Cat()" << endl;if (this -> m_name != nullptr){delete m_name;m_name = nullptr;}}
};void test01()
{Animal * animal = new Cat("Tom");animal ->  Speak();// 通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄露// 解决方法:给基类加一个虚析构函数// 虚析构函数用来解决通过父类指针释放子类对象delete animal;
}int main()
{test01();return 0;
}

6.多态案例三:电脑组装

案例描述
电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

示例

#include <iostream>
using namespace std;// 抽象cpu类
class CPU
{
public:// 抽象计算函数virtual void calculate() = 0;
};// 抽象显卡类
class GPU
{
public:// 抽象显示函数virtual void display() = 0;
};// 抽象内存条类
class Memory
{
public:// 抽象的存储函数virtual void storage() = 0;
};// 电脑类
class Computer
{
public:Computer(CPU* cpu, GPU* gpu, Memory* memory){m_cpu = cpu;m_gpu = gpu;m_memory = memory;}// 提供工作的函数void work(){// 让零件工作起来,调用接口m_cpu->calculate();m_gpu->display();m_memory->storage();}// 提供析构函数,释放3个电脑零件~Computer(){// 释放CPU零件if (m_cpu != nullptr){delete m_cpu;m_cpu = nullptr;}// 释放GPU零件if (m_gpu != nullptr){delete m_gpu;m_gpu = nullptr;}// 释放内存零件if (m_memory != nullptr){delete m_memory;m_memory = nullptr;}}
private:CPU* m_cpu;     // CPU的零件指针GPU* m_gpu;      // 显卡的零件指针Memory* m_memory;      // 内存条的零件指针
};// 具体厂商
//Intel厂商
class IntelCPU: public CPU
{
public:virtual void calculate(){cout << "Intel CPU calculate" << endl;}
};class IntelGpu: public GPU
{
public:virtual void display(){cout << "Intel GPU display" << endl;}
};class IntelMemory: public Memory
{
public:virtual void storage(){cout << "Intel Memory storage" << endl;}
};// lenovo厂商
class LenovoCPU: public CPU
{
public:virtual void calculate(){cout << "Lenovo CPU calculate" << endl;}
};class LenovoGPU: public GPU
{
public:virtual void display(){cout << "Lenovo GPU display" << endl;}
};class LenovoMemory: public Memory
{
public:virtual void storage(){cout << "Lenovo Memory storage" << endl;}
};void test()
{// 第一台电脑零件CPU *intelCpu = new LenovoCPU();GPU *intelGpu = new LenovoGPU();Memory *intelMemory = new LenovoMemory();cout << "第一台电脑开始工作:" << endl;Computer *computer1 = new Computer(intelCpu, intelGpu, intelMemory);computer1->work();delete computer1;cout << "----------------------"  << endl;cout << "第二台电脑开始工作:" << endl;// 第二台电脑组装Computer * computer2 = new Computer(new LenovoCPU, new LenovoGPU, new LenovoMemory);computer2->work();delete computer2;cout  << "----------------------"  << endl;cout << "第三台电脑开始工作:" << endl;// 第三台电脑组装Computer * computer3 = new Computer(new LenovoCPU, new IntelGpu, new LenovoMemory);computer3 ->work();delete computer3;}int main() {test();return 0;
}
http://www.lqws.cn/news/77545.html

相关文章:

  • opencv调用模型
  • cJSON简单使用
  • 定制开发开源AI智能名片驱动下的海报工厂S2B2C商城小程序运营策略——基于社群口碑传播与子市场细分的实证研究
  • 7.4-Creating data loaders for an instruction dataset
  • 【机器学习基础】机器学习入门核心算法:多分类与多标签分类算法
  • 【iOS(swift)笔记-14】App版本不升级时本地数据库sqlite更新逻辑二
  • 如何使用flask做任务调度
  • hot100 -- 6.矩阵系列
  • python打卡day43@浙大疏锦行
  • 3,信号与槽机制
  • Eigen库介绍以及模块划分和相关示例代码
  • NodeJS全栈WEB3面试题——P3Web3.js / Ethers.js 使用
  • Cursor 0.51 全网首歌新功能深度体验:Generate Memories 让 AI 编程助手拥有“记忆“
  • 【DAY37】早停策略和模型权重的保存
  • 微软PowerBI考试 PL-300学习指南
  • 【001】利用github搭建静态网站_essay
  • Go整合Redis2.0发布订阅
  • 6.2本日总结
  • leetcode90.子集II:排序与同层去重的回溯优化策略
  • Python 在金融中的应用- Part 1
  • Pytorch知识点2
  • dify应用探索
  • 【Go语言】Ebiten游戏库开发者文档 (v2.8.8)
  • 字节跳动开源图标库:2000+图标一键换肤的魔法
  • 神经网络中的梯度消失与梯度爆炸
  • 代码随想录60期day54
  • 牛客周赛 Round 94
  • 聚类分析 | MATLAB实现基于SOM自组织特征映射聚类可视化
  • 数据结构之排序
  • 对抗攻击 Adversarial Attack