C++初步之核心编程篇五:多态与虚函数类
C++初步之核心编程篇五:多态与虚函数类
文章目录
- C++初步之核心编程篇五:多态与虚函数类
- 1. 多态的概要
- 2. 多态案例一-计算器类
- 3. 纯虚函数和抽象类
- 4. 多态案例二-制作饮品
- 5. 虚析构和纯虚析构
- 6. 多态案例三-电脑组装
1. 多态的概要
多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
下面的实例中,基类 Shape 被派生为两个类,如下所示:
#include <iostream>
using namespace std;class Shape {protected:int width, height;public:Shape( int a=0, int b=0){width = a;height = b;}int area(){cout << "Parent class area :" <<endl;return 0;}
};
class Rectangle: public Shape{public:Rectangle( int a=0, int b=0):Shape(a, b) { }int area (){ cout << "Rectangle class area :" <<endl;return (width * height); }
};
class Triangle: public Shape{public:Triangle( int a=0, int b=0):Shape(a, b) { }int area (){ cout << "Triangle class area :" <<endl;return (width * height / 2); }
};
// 程序的主函数
int main( )
{Shape *shape;Rectangle rec(10,7);Triangle tri(10,5);// 存储矩形的地址shape = &rec;// 调用矩形的求面积函数 areashape->area();// 存储三角形的地址shape = &tri;// 调用三角形的求面积函数 areashape->area();return 0;
}
运行结果:
Parent class area :
Parent class area :
导致错误输出的原因是,调用函数 area() 被编译器设置为基类中的版本,这就是所谓的静态多态,或静态链接 - 函数调用在程序执行前就准备好了。有时候这也被称为早绑定,因为 area() 函数在程序编译期间就已经设置好了。
但现在,让我们对程序稍作修改,在 Shape 类中,area() 的声明前放置关键字 virtual,如下所示:
class Shape {protected:int width, height;public:Shape( int a=0, int b=0){width = a;height = b;}virtual int area(){cout << "Parent class area :" <<endl;return 0;}
};
运行结果:
Rectangle class area :
Triangle class area :
2. 多态案例一-计算器类
//多态实现
//抽象计算器类
//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
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() {//test01();test02();system("pause");return 0;
}
3. 纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容。因此可以将虚函数改为纯虚函数,纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0 ;
当类中有了纯虚函数,这个类也称为抽象类
抽象类的特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
class Base
{public://纯虚函数//类中只要有一个纯虚函数就称为抽象类virtual void func() = 0;
};class Son :public Base
{public:virtual void func() {cout << "func调用" << endl;};
};void test01()
{Base * base = NULL;//base = new Base; // 错误,抽象类无法实例化对象base = new Son;base->func();delete base;//记得销毁
}int main() {test01();system("pause");return 0;
}
4. 多态案例二-制作饮品
案例描述:
制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料,利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶。
//抽象制作饮品
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* drink) {drink->MakeDrink();delete drink;
}void test01() {DoWork(new Coffee);cout << "--------------" << endl;DoWork(new Tea);
}int main() {test01();system("pause");return 0;
}
5. 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
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:Cat(string name){cout << "Cat构造函数调用!" << endl;m_Name = new string(name);}virtual void Speak(){cout << *m_Name << "小猫在说话!" << endl;}~Cat(){cout << "Cat析构函数调用!" << endl;if (this->m_Name != NULL) {delete m_Name;m_Name = NULL;}}public:string *m_Name;
};void test01()
{Animal *animal = new Cat("Tom");animal->Speak();//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏//怎么解决?给基类增加一个虚析构函数//虚析构函数就是用来解决通过父类指针释放子类对象delete animal;
}int main() {test01();system("pause");return 0;
}
虚析构或纯虚析构就是用来解决通过父类指针释放子类对象,如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
6. 多态案例三-电脑组装
案例描述:
电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作
#include<iostream>
using namespace std;//抽象CPU类
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(){//释放CPU零件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的显卡开始显示了!" << endl;}
};class IntelMemory :public Memory
{public:virtual void storage(){cout << "Intel的内存条开始存储了!" << endl;}
};//Lenovo厂商
class LenovoCPU :public CPU
{public:virtual void calculate(){cout << "Lenovo的CPU开始计算了!" << endl;}
};class LenovoVideoCard :public VideoCard
{public:virtual void display(){cout << "Lenovo的显卡开始显示了!" << endl;}
};class LenovoMemory :public Memory
{public:virtual void storage(){cout << "Lenovo的内存条开始存储了!" << endl;}
};void test01()
{//第一台电脑零件CPU * intelCpu = new IntelCPU;VideoCard * intelCard = new IntelVideoCard;Memory * intelMem = new IntelMemory;cout << "第一台电脑开始工作:" << endl;//创建第一台电脑Computer * computer1 = new Computer(intelCpu, intelCard, 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;}
THE END…
C++初步之核心编程篇五:多态与虚函数类相关推荐
- 零基础学C++——黑马程序员课程笔记(C++核心编程篇)
封面来自互联网侵删 视频地址:点击访问 (我这里开发工具选用VSstudio) 此笔记有三个系列: C++基础语法入门篇 点击查看 C++核心编程篇 C++提高编程篇 本阶段主要针对C++面向对象编程 ...
- 「地表最强」C++核心编程(五)类和对象--对象初始化和清理
环境: 编译器:CLion2021.3:操作系统:macOS Ventura 13.0.1 文章目录 一.构造函数和析构函数 1.1 构造函数 1.2 析构函数 1.3 示例 二.构造函数的分类及调用 ...
- 【C++核心编程篇】
C++核心编程篇 C++核心编程 1 内存分区模型 1.1 程序运行前 1.2 程序运行后 1.3 new操作符 2 引用 2.1 引用的基本使用 2.2 引用注意事项 2.3 引用做函数参数 2.4 ...
- c++核心编程继承和多态 、文件简单读写
4.6 继承 继承是面向对象三大特性之一 有些类与类之间存在特殊的关系,例如下图中: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ePVg2IyK-161000214 ...
- 复习笔记(五)——C++多态和虚函数
静态绑定与动态绑定 静态绑定: -编译时就能确定一条函数调用语句要调用的函数 -在程序编译时多态性体现在函数和运算符的重载上 动态绑定: -运行时才能确定函数调用语句调用的函数 -程序运行时的多态性通 ...
- C++编程思想:继承与虚函数以及多态
文章目录 简介 实现虚函数多态的技术原理 对象切边 析构函数和构造函数中的虚函数 使用继承的类的析构函数应该使用虚函数修饰符 简介 继承与虚函数与多态是浑然一体的三个概念,父类中虚函数可以表现出多态特 ...
- C++ 多态、虚函数、虚方法
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override).通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写 ...
- PKU C++程序设计实习 学习笔记3 多态与虚函数
第六章 多态与虚函数 6.1 多态和虚函数的基本概念 引言 多态是面向对象程序设计里面非常重要的这个机制.它能很有效的提高程序的可扩充性. 有些程序设计语言有被对象继承的概念,但是没有多态的概念,那这 ...
- 【C++】动态多态与虚函数
文章目录 一.动态多态概念 二.虚函数 2.1 虚函数的概念 2.2 虚函数的特点 三.动态多态实现的背后编译器帮我做了什么? 四.虚析构 4.1 解决多态中资源释放的问题 4.2 解决多重继承中的资 ...
最新文章
- 白盒测试工具_别再头疼工作效率低!这些超实用的黑盒、白盒测试方法你都用上了吗?...
- Ansible — Playbooks
- 结构型模式之Composite模式
- 1615: [Usaco2008 Mar]The Loathesome Hay Baler麻烦的干草打包机
- 删零c语言,C语言(请不要删)
- HDOJ 4253 Two Famous Companies 二分+MST
- 素数判断的java方法_java判断数字是否是素数的方法
- python用户标识符条件_使用sum(if…)或条件语句操作两个数据集,这些语句没有hivehadooppython的公共标识符...
- C语言实现2048小游戏
- mac 黑窗口连接mysql_macOS -- Mac系统如何通过终端使用mysql
- linux关闭端口进程命令,linux关闭端口命令
- noip滚泥巴记2016
- 世界上最经典的25句话--摘抄
- 熊猫 PK27QA2 评测
- el-form内el-select与el-input纵向不对齐的问题
- QLabel类常用方法
- Flutter中PlatformView组件无法刷新的问题
- 读《中国思想经典讲稿》
- 记得第一次看见他的时候,他刚被收容所送
- iOS oc 手机摇一摇震动带音效功能
热门文章
- threejs制作3d模型展示网页
- 向量数据库极简教程 | A Gentle Introduction to Vector Databases
- 使用百度地图API,定位并显示自己的位置
- 《LoadRunner虚拟用户开发指南》写作心语
- 解读ACL2020的一篇机器阅读理解方向的论文(Recurrent Chunking Mechanisms for Long-text machine reading comprehension)
- 在html中如何画斜线,在CSS中绘制斜线
- jmeter 保存响应到文件
- Oracle数据库之SQL函数
- 2017科协青少年科技中心--中央电教馆(与科技相关的比赛)
- php游戏实例,php实现的简易扫雷游戏实例_PHP