c++ 多态 运行时多态和编译时多态_C++核心编程 第十一节 多态

前言:多态是C++面向对象三大特性之一。

5e68f8da75f447dec4a324201f2ae4df.png

多态,指的是一个类实例的相同方法在不同情形有不同表现形式。具有不同内部结构的对象可以共享外部接口。C++多态就是用一个更通用的基类指针指向不同的子类实例,为了能调用正确的方法,我们需要用到虚函数和虚继承。

#include 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;}

多态案例——计算器类

案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类。

#include#includeusing namespace std;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;    int m_Num2;};void test(){    // 普通实现测试    Calculator c;    c.m_Num1 = 10;    c.m_Num2 = 20;    cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.m_Num1 + c.m_Num2 << endl;    cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.m_Num1 - c.m_Num2 << endl;    cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.m_Num1 * c.m_Num2 << endl;}// 多态实现// 抽象计算器类// 多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护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 test2(){    // 创建加法计算器    AbstractCalculator *abc = new AddCalculator;    abc->m_Num1 = 10;    abc->m_Num2 = 20;    cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;    delete abc; // 用完了销毁    // 创建减法计算器    abc = new SubCalculator;    abc->m_Num1 = 10;    abc->m_Num2 = 20;    cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;    delete abc;    // 创建乘法计算器    abc = new MulCalculator;    abc->m_Num1 = 10;    abc->m_Num2 = 20;    cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;    delete abc;}int main(){    test();    test2();    return 0;}

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

纯虚函数和抽象类

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

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

多态案例二—制作饮品

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

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

#includeusing namespace std;class AbstractDringking{public:    // 烧水    virtual void Boil() = 0;    // 冲泡    virtual void Brew() = 0;    // 倒入杯中    virtual void PourInCup() = 0;    // 加入辅料    virtual void PutSomething() = 0;    // 规定流程    void MakeDrink(){        Boil();        Brew();        PourInCup();        PutSomething();    }};// 制作茶水class Tea : public AbstractDringking{public:    void Boil(){ cout << "煮农夫山泉" << endl; }    void Brew(){ cout << "冲泡茶叶" << endl; }    void PourInCup(){ cout << "将茶水倒入杯中" << endl; }    void PutSomething(){ cout << "加入枸杞" << endl; }};// 制作咖啡class Coffee : public AbstractDringking{public:    void Boil(){ cout << "煮纯净水" << endl; }    void Brew(){ cout << "冲泡咖啡" << endl; }    void PourInCup(){ cout << "将咖啡倒入杯中" << endl; }    void PutSomething(){ cout << "加入牛奶" << endl; }};// 业务函数void Dowork(AbstractDringking* drink){    drink->MakeDrink();    delete drink;}void test(){    Dowork(new Tea);    cout << "----------------------" << endl;    Dowork(new Coffee);}int main(){    test();    return 0;}

虚析构和纯虚析构

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

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

#includeusing 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:    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;    }    string *m_Name;};void test(){    Animal *animal = new Cat("Tom");    animal->Speak();    // 通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄露    // 怎么解决?给基类增加一个虚析构函数    // 虚析构函数就是用来解决父类指针释放子类对象    delete animal;}int main(){    test();    return 0;}

总结:

1、虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2、如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3、拥有纯虚析构函数的类也属于抽象类

多态案例三 - 电脑组装

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

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

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

#includeusing 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 * men){        m_cpu = cpu;        m_vc = vc;        m_men = men;    }    // 提供工作的函数    void work(){        // 让零件工作起来,调用接口        m_cpu->calculate();        m_vc->display();        m_men->storage();    }    // 提供析构函数,释放三个电脑零件    ~Computer(){        if(m_cpu != NULL){            delete m_cpu;            m_cpu = NULL;        }        if(m_vc != NULL){            delete m_vc;            m_vc = NULL;        }        if(m_men != NULL){            delete m_men;            m_men = NULL;        }    }private:    CPU * m_cpu;    VideoCard * m_vc;    Memory * m_men;};// 具体厂商// 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 test(){    // 第一条电脑零件    CPU * intelCpu = new IntelCPU;    VideoCard * intelCard = new IntelVideoCard;    Memory * intelMem = new IntelMemory;    cout << "Intel零件组装电脑开始工作:" << endl;    // 创建第一台电脑    Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);    computer1->work();    delete computer1;    cout << "------------------------" << endl;    cout << "Lenovo零件组装电脑开始工作:" << 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(){    test();    return 0;}

                        - END -

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

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

相关文章

oracle sql 子游标_Oracle 父子游标

游标从Oracle数据库管理员的角度上说&#xff0c;游标是对存储在库缓存中的可执行对象的统称。SQL语句是存储在库缓存中的&#xff0c;它是游标。除了它之外&#xff0c;还有Oracle的存储过程也是存储在库缓存中的可执行对象&#xff0c;从Oracle DBA的角度上说&#xff0c;它也…

多线程操作时操作系统时间片_从零开始自制操作系统(15):内核多线程

1.多线程原理&#xff1a;&#xff08;1&#xff09;概述&#xff1a;多线程是指CPU可以在一段时间中并行执行多个程序&#xff0c;比如我们可以一边听音乐、一边写代码&#xff08;这两个程序可以“同时进行”&#xff0c;我们称之为多进程&#xff0c;而多进程实现的本质就是…

听说java又过气了?看我运用大数据分析2019年java发展趋势!

近些年的技术圈&#xff0c;单以计算机语言界来说&#xff0c;稳坐第一把太师椅的 Java “或将被取代”、迎接转折点、Java 项目工程师风光不再等言论不绝于耳。在焦虑的大环境下&#xff0c;所有人好像都看起来很焦虑不安。 针对这类“唱衰论”&#xff0c;也不难理解。不仅&…

打开git界面_使用 Gitea 快速搭建私有 Git 版本控制服务

1. 前言分布式版本控制工具 Git 已经是现代软件源代码版本控制首选方案之一。公有 Git 服务提供商 国外知名如 GitHub 国内网络延迟高,Gitlab 涉嫌对中国的歧视不推荐。国内有 Gitee、Coding 生态还不错。但是一般公司的源代码除非开源项目是不会放在公有 Git 服务上的。所以我…

dev项目属性按钮是灰色_Spring Boot 中的项目属性配置

阅读本文约需要5分钟大家好&#xff0c;我是你们的导师&#xff0c;我每天都会在这里给大家分享一些干货内容(当然了&#xff0c;周末也要允许老师休息一下哈)。上次老师跟大家分享了Spring Boot 如何使用 SLF4J 进行日志记录&#xff0c;今天跟大家分享一下 Spring Boot 中的项…

diskgenius扩容c盘重启电脑卡住_电脑开机显示:reboot and select proper boot device怎么办?...

今天就碰到有一个知友问&#xff0c;自己电脑开机就提示&#xff1a;reboot and select proper boot device&#xff0c;整个人都懵了&#xff0c;不知道怎么办?其实对于电脑出现问题&#xff0c;大家不要着急&#xff0c;坚哥就来为大家分析下原因以及试着去解决。第一种原因…

2019年1月份GitHub上最热门的Java开源项目

相信大多数程序猿们都回归工作岗位啦&#xff0c;不知道是否调整好心态了呢&#xff1f;1月份GitHub上最热门的Java开源项目新鲜出炉&#xff0c;还是一起来看看都有哪些项目上榜吧&#xff1a; 1JavaGuide https://github.com/Snailclimb/JavaGuide Star 22668 【Java学习面试…

大并发下程序出错_Python并发编程理论篇

前言其实关于Python的并发编程是比较难写的一章&#xff0c;因为涉及到的知识很复杂并且理论偏多&#xff0c;所以在这里我尽量的用一些非常简明的语言来尽可能的将它描述清楚&#xff0c;在学习之前首先要记住一个点&#xff1a; 并发编程永远的宗旨就是提高程序的运行效率&am…

2020 idea 查看内存消耗_idea内存如何设置

更具自己硬件环境做出相应的调整.在这里简单说一下这些参数作用以及参考更改参数-Xms768m-Xmx768m-XX:MaxPermSize250m-XX:ReservedCodeCacheSize64m-server-XX:NewSize128m-XX:UseParNewGC-XX:ParallelGCThreads8-XX:UseConcMarkSweepGC-XX:CMSInitiatingOccupancyFraction60-…

月薪30K程序员花了一个小时,用c++做出经典扫雷游戏 !

上次发过一个俄罗斯方块的游戏源码&#xff0c;由于是通过Easy X实现的&#xff0c;但是很多和我一样的新手&#xff0c;一开始不知道Easy X是什么&#xff0c;到时源码拿过去之后&#xff0c;运行报错&#xff0c;我这次发的扫雷&#xff0c; 也是通过Easy X实现&#xff0c;…

用python写web网页_从零开始,使用python快速开发web站点(1) | 学步园

环境&#xff1a;ubuntu 12.04 python版本: 2.73 ok,首先&#xff0c;既然是从零开始&#xff0c;我们需要的是一台可以运行的python的计算机环境&#xff0c;并且假设你已经安装好了python, 然后&#xff0c;既然是快速开发&#xff0c;必不可少的需要用到框架&#xff0c;py…

修改so_货代、海运操作、船务操作还分不清?船公司SO文件看不懂?

货代是货物代理&#xff08;freight forwarding agent&#xff09;的简称&#xff0c;是指经营受他人委托&#xff0c;为其提供代办运输手续&#xff0c;代提、代发、代运货物服务的业务。货物代理&#xff0c;有些是中间商就是自己没有船或者飞机的或者船公司、航空公司&#…

一行代码蒸发64亿人民币!黑客盯上区块链漏洞!Python真的变态!

此前认为&#xff0c;区块链技术由于分布存储、加密算法等技术的应用&#xff0c;拥有了不可篡改、可追溯等被认为是“万无一失”的特性。然而&#xff0c;该特性主要针对存储在区块中的信息来说&#xff0c;以文中开头的案例为例&#xff0c;区块链技术保障了可以追溯到这64亿…

太吾绘卷第一世攻略_建平中学高二数学周练卷(2020.09)

试卷图片仅供学习交流使用&#xff0c;答案仅供参考【往期内容】高一是坎, 高二是坡, 高三是峰! 最全学习攻略新高一数学教材必修第一册第一章习题答案往年高中9月开学考月考数学试卷汇总2020上海高考复交综评录取率top202020北京大学强基计划数学试题2020上海16区零志愿、名额…

用python编写最简单的记事本_Python使用字典实现的简单记事本功能示例

本文实例讲述了Python使用字典实现的简单记事本功能。分享给大家供大家参考&#xff0c;具体如下&#xff1a; from time import sleep, strftime user "Cytus" calendar {} def welcome(): print "Welcome to use this calendar. %s" % user print &quo…

多线程百度网盘爬虫Python完整源码

#coding: utf8import re #正则表达式模块import urllib2 #获取URLs的组件import timefrom Queue import Queueimport threading, errno, datetimeimport jsonimport requests #Requests is an Apache2 Licensed HTTP libraryimport MySQLdb as mdbDB_HOST 127.0.0.1DB_USER r…

自动补足算法是什么_数据、算法岗的几点经验分享!

learners | 作者Datawhale | 来源目录有哪些好的秋招经验分享&#xff1f;机器学习中常用的最优化方法有哪些&#xff1f;想通过数据竞赛来提升实践能力&#xff0c;作为小白有什么入门经验&#xff1f;(今日问题)有哪些好的秋招经验分享&#xff1f;1李玲 - 携程算法工程师(…

python支持中文路径_打开python遍历中文目录得到的文件路径报错

各位朋友们好&#xff0c;我是一名python新手&#xff0c;现在遇到一个问题&#xff0c;恳请各位朋友们能指点一下我。 我的环境是:ubuntu14.04系统,python2.7 用python遍历一个目录&#xff0c;目录层次结构如下: 这是主目录: /home/chaoma/superboard/mydata/input/productio…

我精心珍藏的Python代码技巧

01.****简洁的表达式 image 点评&#xff1a;Python因为简洁高效而出名&#xff0c;就是因为语法非常简单&#xff0c;而且内置了很多强大的数据结构&#xff1a; 比如我们可以大量用推导列表来生成很多简洁的代码 比如我们可以用if else组合&#xff0c;本来需要2-3行代码写…

平移刚体上各点的加速度和速度_大物学习笔记(一)——刚体力学

第一次写笔记&#xff0c;或多或少都会有很多的不足。疫情期间一直都是网课的学习&#xff0c;书本也没有带回来&#xff0c;经常都是在草稿本上随手推导一下公式&#xff0c;隔几天翻了几页&#xff0c;自己推导的东西就不见了。所以总想着来总结一下&#xff0c;把学的东西稍…