C++(9) 虚函数

文章目录

      • 虚函数
        • 1. 虚函数
          • 1.1 虚函数案例1
          • 1.2 虚函数案例2
          • 1.2 纯虚函数
          • 1.3 纯虚函数语法要求总环
          • 1.4 纯虚函数应用
            • 1.4.1 生活案例
            • 1.4.2 虚函数引用代码

虚函数

1. 虚函数
1.1 虚函数案例1
#include <iostream>

using namespace std;

class Animal
{
public:
    // Animal 类内 eat 函数加入的 virtual 修饰
    virtual void eat() { cout << "动物吃饭" << endl; }
};

class Dog : public Animal
{
public:
    void eat() { cout << "Dog 吃饭" << endl; }
};
class Cat : public Animal
{
public:
    void eat() { cout << "Cat 吃饭" << endl; }
};
class Bird : public Animal
{
public:
    void eat() { cout << "Bird 吃饭" << endl; }
};

// 当前函数所需类型为 Animal 类型
void test_eat(Animal *a);

int main(int argc, char const *argv[])
{
    Animal *a = new Animal;
    Dog *dog = new Dog;
    Cat *cat = new Cat;
    Bird *bird = new Bird;

    /*
    test_eat 函数可以使用 dog cat 和 bird 作为实际参数
    【注意】
        当前函数所需参数为 Animal 类型,使用 dog cat 和 bird 作为实际参数
        是符合开发要求的,因为 dog cat 和 bird 都是 Animal 的子类,
        可以看作是【Animal】类型
        从某种意义上来说,dog cat 和 bird 都是 Animal 类型
    【知识点】
        函数参数为父类类型,可以使用父类本身或者子类作为函数的实际参数
    */ 
    test_eat(dog);
    test_eat(cat);
    test_eat(bird);

    return 0;
}

void test_eat(Animal *a)
{
    /*
    当前情况下,使用子类作为当前函数的参数,可以执行对应的代码,
    单数执行的内容是 Animal 内容,非对应子类内容

    因为对于编译器而言,a 是 Animal 类型,编译器会首先选择调用
    Animal 类当前中的 eat 函数

    期望的是:传入的具体数据类型,也就是子类的函数被执行

    父类的指针指向子类的对象,并且想调用子类的函数,这个操作很 virtual

    父类函数没有 virtual 修饰之前
        动物吃饭
        动物吃饭
        动物吃饭
    
    父类函数使用 virtual 修饰之后
        Dog 吃饭
        Cat 吃饭
        Bird 吃饭
        编译器根据【真实类型】调用目标函数
    */
    a->eat();
}
1.2 虚函数案例2
#include <iostream> 

using namespace std;

class Base
{
public:
    virtual void test() { cout << "Base 类 test 函数" << endl; }
};

class Sub : public Base
{
public:
    void test() { cout << "Sub 类 test 函数" << endl; } 
};

int main(int argc, char const *argv[])
{
    Base * b = new Sub;

    b->test();

    delete b;

    return 0;
}
1.2 纯虚函数
#include <iostream> 

using namespace std;


class Base
{
public:
    /*
    virtual 修饰的虚函数,存在函数体,子类通过继承之后,
    得到当前函数内容,并且执行使用
    */
    virtual void test() { cout << "虚函数" << endl; }

    /*
    【纯】虚函数
        1. 使用 virtual 修饰
        2. 函数有且只有声明
        3. 函数声明之后又 = 0 操作,特定格式!
        告知编译器当前函数没有函数体,只有函数声明!
    */
    virtual void test_pure() = 0;
};

/*
Sub 是 Base 类中的一个子类,继承 Base 类中的 public 和 protected 修饰内容
当前 Sub 从 Base 类中获取到
    virtual void test() {......} 存在函数体
    virtual void test_pure() = 0; 没有函数体的虚函数

提示报错:
不允许使用抽象类类型 "Sub" 的对象:C/C++(322)
02_纯虚函数.cpp(42, 18): 纯虚拟 函数 "Base::test_pure" 没有强制替代项

报错中可以获取到的信息:
    1. 一个类内存在【纯虚函数】,当前类 C++ 认为是一个【抽象 abstract 类】
    2. 纯虚函数没有函数体,如果实例化对象想要调用纯虚函数,语法错误,所有编译器
        前置语法报错告知【纯虚拟 函数 "Base::test_pure" 没有强制代替项】
        理解为:子类继承父类的纯虚函数,需要【实现/完成函数体】

*/
class Sub : public Base
{
public:
    void test_pure() { cout << "子类【强制】实现纯虚函数" << endl; }
};

int main(int argc, char const *argv[])
{
    Sub *m = new Sub;

    m->test();
    m->test_pure();

    delete m;
    
    return 0;
}
1.3 纯虚函数语法要求总环
  • 纯虚函数没有函数体,只有函数声明和特定的 = 0 格式
  • 纯虚函数所在的类,是一个【抽象 abstract 类】
  • 抽象无法进行实例化操作。因为抽象类有纯虚函数没有函数体,实例化对象无法调用,所有语法前置报错,提示不能实例化
  • 子类继承抽象类,如果实现抽象类中的纯虚函数,子类不再是一个抽象类,如果未实现,还是一个抽象类
1.4 纯虚函数应用
1.4.1 生活案例

在这里插入图片描述

1.4.2 虚函数引用代码
#include <iostream>

using namespace std;

// Base 是一个【抽象类】
class Base
{
public:
    // 使用【纯虚函数】制定函数实现规则,限制【函数三要素:返回值类型、函数名、形式参数列表】
    virtual void boil() = 0;
    virtual void make_tea() = 0;
    virtual void add_spices() = 0;
    virtual void seal_cup() = 0;
};

/*
通用的 Base.实现 Base 抽象类中部分函数
    泡茶喝添加小料操作尚未实现,具备一定的【特征性和唯一性】
*/
class GenericBase : public Base
{
public:
    virtual void boil() { cout << "纯净水烧制 100 ° " << endl; }
    virtual void seal_cup() { cout << "自动封杯机工作" << endl; }
};

/*
【实现类】
    具备一定的特征性和使用性
*/
class BYJX : public GenericBase
{
public: 
    BYJX() { name = "伯牙绝弦"; }

    void make_tea() { cout << "茉莉花茶" << endl; }
    void add_spices() { cout << "牛乳" << endl; }

    string getName() { return name; } 

private:
    string name;
};

class QGPE : public  GenericBase
{
public:
    QGPE() { name = "青柑普洱"; }

    void make_tea() { cout << "青柑普洱" << endl; }
    void add_spices() { cout << "三分糖" << endl; }

    string getName() { return name; }
private: 
    string name;
};

class GreenTea : public GenericBase
{
public:
    GreenTea() { name = "绿茶"; }

    void make_tea() { cout << "信阳毛尖" << endl; }
    void add_spices() { cout << "三分糖" << endl; }

    string getName() { return name; }

private:    
    string name;
};

class Frappuccino : public GenericBase
{
public:
    Frappuccino() { name = "摩卡可可碎星冰乐"; }

    void boil() { cout << "常温纯净水" << endl; }
    void make_tea() { cout << "咖啡" << endl; }
    void add_spices() { cout << "摩卡酱 + 可可碎" << endl; }
    void seal_cup() { cout << "自动封杯机工作" << endl; }

    string getName() { return name; } 
private:
    string name;
};

int main(int argc, char const *argv[])
{
    BYJX *b = new BYJX;

    b->boil(); // 纯净水烧制 100 ° 
    b->make_tea(); // 茉莉花茶
    b->add_spices(); // 牛乳
    b->seal_cup(); // 自动封杯机工作
    cout << b->getName() << endl; // 伯牙绝弦
    cout << "------------------------" << endl;

    QGPE *q = new QGPE;

    q->boil(); // 纯净水烧制 100 ° 
    q->make_tea(); // 青柑普洱
    q->add_spices(); // 三分糖
    q->seal_cup(); // 自动封杯机工作
    cout << q->getName() << endl; // 青柑普洱
    cout << "------------------------" << endl;

    GreenTea *g = new GreenTea;

    g->boil(); // 纯净水烧制 100 ° 
    g->make_tea(); // 信阳毛尖
    g->add_spices(); // 三分糖
    g->seal_cup(); // 自动封杯机工作
    cout << g->getName() << endl; // 绿茶
    cout << "------------------------" << endl;

    Frappuccino *f = new Frappuccino;

    f->boil(); // 纯净水烧制 100 ° 
    f->make_tea(); // 咖啡
    f->add_spices(); // 摩卡酱 + 可可碎
    f->seal_cup(); // 自动封杯机工作
    cout << f->getName() << endl; // 摩卡可可碎星冰乐

    delete b;
    delete q;
    delete g;
    delete f;

    return 0;
}
----------------------" << endl;

    Frappuccino *f = new Frappuccino;

    f->boil(); // 纯净水烧制 100 ° 
    f->make_tea(); // 咖啡
    f->add_spices(); // 摩卡酱 + 可可碎
    f->seal_cup(); // 自动封杯机工作
    cout << f->getName() << endl; // 摩卡可可碎星冰乐

    delete b;
    delete q;
    delete g;
    delete f;

    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

神经网络与深度学习Pytorch版 Softmax回归 笔记

Softmax回归 目录 Softmax回归 1. 独热编码 2. Softmax回归的网络架构是一个单层的全连接神经网络。 3. Softmax回归模型概述及其在多分类问题中的应用 4. Softmax运算在多分类问题中的应用及其数学原理 5. 小批量样本分类的矢量计算表达式 6. 交叉熵损失函数 7. 模型预…

VMware虚拟机安装统信uos桌面专业版操作系统系统

统信uos桌面版版本对比:https://www.uniontech.com/next/product/desktop-contrast专业版只要是面向政企等单位,这里只是用虚拟机安装测试基本功能使用,对于我们个人要长期使用的话可以使用家庭版或者社区版 1镜像下载 1.1打开官网 镜像在统信生态社区下载统信生态社区官网:…

【pytest系列】- assert断言的使用

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

Vue打包后,页面样式出现混乱,刷新即可恢复正常

错误效果 本地上的样式完全正常&#xff0c;打包后的页面第一次进入出现样式错误的现象&#xff0c;刷新一次就恢复正常了 正确效果 找到问题 异常情况是右上方我圈出来的部分&#xff0c; .spContent .conTitle{} 是这个Live Video的样式类&#xff0c; 但是页面中也使用了…

Vue之状态管理的简单使用(事件总线(Event Bus),Vuex和若依前端示例)

文章目录 Vue之状态管理的简单使用&#xff08;事件总线&#xff08;Event Bus&#xff09;&#xff0c;Vuex和若依前端示例&#xff09;Vue之事件总线&#xff08;Event Bus&#xff09;的简单使用Vuex进行状态管理的简单使用若依前端代码store状态管理&#xff1a; Vue之状态…

机械师K7 87键盘拆解螺丝位置

拆键盘不需要拔掉所有键盘帽,只需要拔掉螺丝位置所在的两侧键帽就可以. 红色①-⑮是螺丝所在位置,一般在两个键帽中间下面位置就是螺丝. 拆开以后电池仓尺寸:4CM*9CM , 适合的电池型号:504080型 50:电池厚度50mm 40:电池宽度40mm 80:电池长度80mm 插头是:1.25红黑头 TB都可以买…

VR全景技术如何运用在文旅展示,VR全景技术对景区有哪些好处

引言&#xff1a; 随着科技的不断进步和社会的不断发展&#xff0c;VR全景技术越来越受到人们的关注。在文化旅游行业中&#xff0c;VR全景技术的应用为景区提供了全新的展示方式和体验内容&#xff0c;极大地丰富了游客的文化旅游体验。那么VR全景技术能给文旅展示带来哪些好…

Windows 10 系统重装太简单了,U盘、PE已经成为历史

虽时至今日&#xff0c;Windows 系统的越来越现代化&#xff0c;连安装方式越简单很多。使用微软官方的安装程序&#xff0c;几乎可以像安装软件一样安装系统。本文将推荐几种小淙比较喜欢的 Windows 10 系统重装方式&#xff0c;大家可以按照教程说明操作。 注意&#xff1a;…

Hive中left join 中的where 和 on的区别

目录 一、知识点 二、测试验证 三、引申 一、知识点 left join中关于where和on条件的知识点&#xff1a; 多表left join 是会生成一张临时表。on后面&#xff1a; 一般是对left join 的右表进行条件过滤&#xff0c;会返回左表中的所有行&#xff0c;而右表中没有匹配上的数…

Unity中开发程序打包发布

添加ESC脚本 使用Unity打包发布的过程中&#xff0c;考虑到打开的程序会处于全屏界面&#xff0c;而此时我们又会有退出全屏的需求&#xff0c;因此需要添加ESC脚本&#xff0c;当我们单击ESC脚本的过程中&#xff0c;退出全屏模式。 在Assets/Scenes下&#xff0c;创建esc.cs…

正弦波拟合

正弦波拟合是一种常见的数学方法&#xff0c;用于确定最佳匹配给定数据集的正弦波形。这可以用于各种应用&#xff0c;如信号处理、周期性数据分析等。以下举例展示如何进行正弦波拟合。 步骤与方法 收集数据&#xff1a;首先&#xff0c;你需要收集或生成一组数据&#xff0…

陪诊小程序系统专业开发:从需求分析到用户体验

随着社会老龄化程度的加深&#xff0c;以及人们对医疗服务需求的增加&#xff0c;陪诊服务逐渐成为一种重要的社会服务。为了满足这种需求&#xff0c;陪诊小程序系统应运而生。本文将探讨陪诊小程序系统的专业开发&#xff0c;包括需求分析、系统设计、技术实现和用户体验等方…

《区块链简易速速上手小册》第8章:区块链的技术挑战(2024 最新版)

文章目录 8.1 可扩展性问题8.1.1 基础知识8.1.2 主要案例&#xff1a;比特币的可扩展性挑战8.1.3 拓展案例 1&#xff1a;以太坊的可扩展性改进8.1.4 拓展案例 2&#xff1a;侧链和分层解决方案 8.2 安全性与隐私8.2.1 基础知识8.2.2 主要案例&#xff1a;比特币交易的安全性8.…

若依框架快速入门

首先看若依文档 一般情况下&#xff0c; ruoyi-system是储存后台代码的&#xff0c; ruoyi-ui是储存前端代码的 1.拉取代码 2.若依的结构 3.运行SQL脚本 sql文件夹中是有两个sql脚本的&#xff0c;这两个脚本都是若依开源项目的必须脚本&#xff0c;需要运行在你自己 的数…

Django中的模板

目录 一:基本概念 二&#xff1a;模板继承 在Django中&#xff0c;模板是用于呈现动态内容的HTML文件。它们允许你将动态数据与静态模板结合起来&#xff0c;生成最终的HTML页面。 Django模板使用特定的语法和标签来插入动态内容。你可以在模板中使用变量、过滤器和标签来控…

XVC768AE102 3BHB007211R0102

XVC768AE102 3BHB007211R0102 XVC768AE102 3BHB007211R0102 基于CAN总线接口和模块控制器实现组合机床电控通信系统的设计 "... 并联组合机床电控的数据采集模块和CAN总线组成。 本文研究的 ... 。 监控主机通过CAN总线从各模块获取现场控制数据&#xff0c;监控整个系…

头戴式耳机什么牌子性价比高?公认高性价比的头戴式耳机推荐

头戴式耳机作为现代音乐与声音体验的必备品&#xff0c;一直以来都备受消费者的关注&#xff0c;那么&#xff0c;在众多的品牌中&#xff0c;哪些头戴式耳机的性价比最高呢&#xff1f;本文将为你揭晓这个秘密&#xff0c;推荐一些公认的高性价比头戴式耳机&#xff0c;让你在…

C/C++实现无序入参的命令解析工具

C/C实现无序入参的命令解析工具 1 实现思路2 主要功能3 效果展示3.1 直接运行代码图3.2help命令执行效果图3.3命令行执行命令解析效果图 4 代码实现5 代码下载 1 实现思路 基本介绍&#xff1a; 思路来源于atlas,atc(模型转换工具)&#xff0c;该工具对命令支持众多&#xff0…

Unity 导入图片时,如何自动设置格式

AssetPostprocessor是Unity中的一个强大工具&#xff0c;它允许您控制在导入资源时发生的事情。它主要被用来处理在导入或重新导入资源时自定义编程操作的需求。 AssetPostprocessor的主要作用&#xff1a; 1. 自定义资源导入设置&#xff1a;可以自定义图片&#xff0c;模型&…

TensorFlow2实战-系列教程4:数据增强

&#x1f9e1;&#x1f49b;&#x1f49a;TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 猫狗识别1 数据增强 猫狗识别2------数据增强 猫狗识别3------迁移学习 对于图像数据…