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

八、C++速通秘籍—动态多态(运行期)

上一章节:

七、C++速通秘籍—静态多态(编译期)-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/146999362?spm=1001.2014.3001.5502

本章节代码:

cpp/dynamicPolymorphic.cpp · CuiQingCheng/cppstudy - 码云 - 开源中国https://gitee.com/cuiqingcheng/cppstudy/blob/master/cpp/dynamicPolymorphic.cpp

目录

上一章节:

本章节代码:

一、引言

二、概念:什么是运行时多态?

三、实现机制:虚函数

虚函数表和虚函数表指针的作用

四、纯虚函数,抽象类/接口类

抽象类

接口类

五、多态的优点和应用场景

优点

应用场景

六、总结

下一章节:


一、引言

        在 C++ 的奇妙世界里,多态是一个强大而迷人的特性,它就像代码世界中的变形金刚,让程序能够根据不同的情况展现出多样的行为。而 运行期多态,作为多态的重要组成部分 ,更是为程序带来了无与伦比的灵活性和扩展性。今天,我们就一起来深入探秘 C++ 运行期多态的奥秘。

二、概念:什么是运行时多态?

        运行期多态,也被称为 动态多态 ,是指在 程序运行时才确定具体要调用哪个函数 。它通过 虚函数和继承机制来实现,允许我们使用基类的指针或引用调用派生类的函数,从而实现不同对象的不同行为 。简单来说,就是在运行时 根据实际对象的类型来决定执行哪个函数 ,就像变形金刚在战斗中根据不同的场景变换形态一样。

三、实现机制:虚函数

        虚函数是实现运行期多态的关键。在基类中,我们使用 “ virtual 关键字来声明虚函数, 派生类可以重写这些虚函数以实现自己的特定行为 。当通过基类的指针或引用调用虚函数时,程序会在运行时根据指针或引用实际指向的对象类型来决定调用哪个版本的函数。
class base{
    public:
        virtual void func() // 虚函数
        { }
}

class A:public base{
    public:
        void func() override 
        {
            // 重写实现虚函数        
        }
}
每一个 包含虚函数的类中均有一个虚函数表指针,指向虚函数表;如下图:

虚函数表和虚函数表指针的作用

虚函数表( VTable): 每个 包含虚函数的类都有一个虚函数表,这是一个存储虚函数地址的数组 。表中的每个条目都是一个指向虚函数的指针,这些虚函数按照它们在类中声明的顺序排列。
虚函数表指针( VPTR): 每个包含虚函数的类的对象都有一个虚函数表指针,它指向该类对应的虚函数表 。借助这个指针,对象能够在运行时找到正确的虚函数来调用。
实例:一个动物的基类,均有叫这个实现函数,对于两个继承自动物类的狗类/猫类,分别实现其叫这个函数,代码如下:
/***
 *  C++ 多态
 *      动态多态
 *          虚函数/纯虚函数
 *          抽象类/接口类
 */

 #include <iostream>

// 基类 Animal
class Animal {
public:
    // 虚函数 speak()
    virtual void speak() {
        std::cout << "动物发出声音" << std::endl;
    }
};

// 派生类 Cat
class Cat : public Animal {
public:
    // 重写 speak() 函数
    void speak() override {
        std::cout << "喵~" << std::endl;
    }
};

// 派生类 Dog
class Dog : public Animal {
public:
    // 重写 speak() 函数
    void speak() override {
        std::cout << "汪!" << std::endl;
    }
};

int main() {
    // 创建 Cat 和 Dog 对象
    Cat cat;
    Dog dog;

    // 使用基类指针指向派生类对象
    Animal* animal1 = &cat;
    Animal* animal2 = &dog;

    // 调用虚函数
    animal1->speak(); // 输出:喵~
    animal2->speak(); // 输出:汪!

    animal1->Animal::speak(); // 输出:动物发出声音
    return 0;
}
这里 基类的指针,会根据实际指向的对象类型,从而决定默认调用的是重写的虚函数 ,若还想调用基类原来的虚函数,需要指定作为基类方可调用;

四、纯虚函数,抽象类/接口类

上面定义的基类中,虚函数还是实现了,若 一个虚函数没有实现的代码段体,则是纯虚函数,定义如下:
virtual int func() = 0;

抽象类

        包含纯虚函数的类被称为抽象类 抽象类不能被实例化 ,只能作为基类被派生类继承。 派生类必须重写抽象类中的纯虚函数,否则派生类也会成为抽象类。只有实现了纯虚函数的派生类,才能实例化对象。

接口类

        特殊的抽象类, 类中所有函数都是纯虚函数
实例如下:
/***
 *  C++ 多态
 *      动态多态
 *          虚函数/纯虚函数
 *          抽象类/接口类
 */

 #include <iostream>

 // 抽象类
 class Organism{
public:
    virtual void growup() = 0;
    virtual void eating(){
        std::cout << "eating everything" << std::endl;
    };
 };

// 基类 Animal 还是抽象类,无法实例化对象
class Animal : public Organism{
public:
    // 虚函数 speak()
    virtual void speak() {
        std::cout << "动物发出声音" << std::endl;
    }
};

// 派生类 Cat
class Cat : public Animal {
public:
    void growup(){
        std::cout<< "猫越长越大" << std::endl;
    }
    // 重写 speak() 函数
    void speak() override {
        std::cout << "喵~" << std::endl;
    }
};

// 派生类 Dog
class Dog : public Animal {
public:
    void growup(){
        std::cout<< "狗越长越大" << std::endl;
    }
    // 重写 speak() 函数
    void speak() override {
        std::cout << "汪!" << std::endl;
    }
};

int main() {
    // 创建 Cat 和 Dog 对象
    Cat cat;
    Dog dog;

    // 使用基类指针指向派生类对象
    Animal* animal1 = &cat;
    Animal* animal2 = &dog;

    // 调用虚函数
    animal1->speak(); // 输出:喵~
    animal2->speak(); // 输出:汪!
    animal1->growup(); // 输出:喵~
    animal2->growup(); // 输出:汪!

    animal1->Animal::speak(); // 输出:动物发出声音
    return 0;
}

五、多态的优点和应用场景

优点

  1. 灵活性和扩展性:运行期多态允许我们在不修改现有代码的情况下,轻松地添加新的派生类和功能
  2. 代码复用:通过使用基类的指针或引用,我们可以编写通用的代码来处理不同类型的对象,从而提高代码的复用性。例如,图形绘制程序中,我们可以编写一个通用的函数来计算不同图形的面积,而不需要为每种图形编写单独的函数。
  3. 可维护性:运行期多态使得代码的结构更加清晰,易于理解和维护。通过将不同的行为封装在不同的派生类中,我们可以避免代码的冗余和混乱。

应用场景

  1. 游戏开发:在游戏开发中,运行期多态可以用于实现不同角色的不同行为,如攻击、防御、移动等。例如,不同类型的角色(如战士、法师、刺客)可以继承自一个基类 Character,并分别重写 attack()defend()move() 等函数,以实现不同的行为。
  2. 图形处理:在图形处理中,运行期多态可以用于实现不同图形的不同绘制方法。例如,不同类型的图形(如圆形、矩形、三角形)可以继承自一个基类 Shape,并分别重写 draw() 函数,以实现不同的绘制方法。
  3. 数据库访问:在数据库访问中,运行期多态可以用于实现不同数据库的不同访问方法。例如,不同类型的数据库(如 MySQL、Oracle、SQLite)可以继承自一个基类 Database并分别重写 connect()query()insert() 等函数,以实现不同的访问方法。

六、总结

        运行期多态是 C++ 中一个非常强大和重要的特性,它通过虚函数和继承机制实现了在程序运行时根据实际对象的类型来决定执行哪个函数的功能。

下一章节:

九、C++速通秘籍—类和函数-CSDN博客https://blog.csdn.net/weixin_36323170/article/details/147017358?spm=1001.2014.3001.5502

相关文章:

  • 【蓝桥杯】搜索算法:剪枝技巧+记忆化搜索
  • SpringBoot类跨包扫描失效的几种解决方法
  • SpringBoot企业级开发之【用户模块-登录】
  • 群晖NAS的最好的下载软件/电影的方式(虚拟机安装win系统安装下载软件)
  • 【5分钟论文阅读】InstructRestore论文解读
  • linux-core分析 : sip变量赋值-指针悬挂
  • 【LeetCode】算法详解#3 ---最大子数组和
  • 人工智能新时代:从深度学习到自主智能
  • 人工智能:深度学习关键技术与原理详解
  • LeetCode 解题思路 30(Hot 100)
  • 硬盘分区格式之GPT(GUID Partition Table)笔记250407
  • 【k8s学习之CSI】理解 LVM 存储概念和相关操作
  • 喂饭教程-Dify如何集成RAGFlow知识库
  • [ISP] ISP 中的 GTM 与 LTM:原理、算法与与 Gamma 校正的对比详解
  • Token+JWT+Redis 实现鉴权机制
  • 2024年十五届蓝桥杯青少年Scratch省赛初级组——找不同
  • 极空间NAS进阶玩法:Debian 系统安装教程
  • Docker学习--卷相关命令
  • 瓦片数据合并方法
  • 【R语言绘图】圈图绘制代码
  • 张汝伦:康德和种族主义
  • 人民日报民生观:转人工客服,怎么这么难?
  • 中国乒协坚决抵制恶意造谣,刘国梁21日将前往多哈参加国际乒联会议
  • 外交部:中方对美芬太尼反制仍然有效
  • “异常”只停留在医院里,用艺术为“泡泡宝贝”加油
  • 乌拉圭前总统何塞·穆希卡去世