深入了解C++多态的原理及实现方式

2023-05-16

文章目录

  • 前言
  • 关于多态
  • 函数承载方式
  • 虚函数方式

前言

需要深入了解C/C++语言的基础之上再看此文章。

关于多态

具有多种形态,调用同一个方法会随上下文不同而产生不同的结果,多态有静态多态与动态多态两种。

函数承载方式

函数重载是让同一个函数(函数名字是相同的)可以根据参数不同从而实现不同的处理,之所以称之为函数重载,是因为它有多个同名的函数,而编译器进行了重载,编译器根据实参与形参的类型及个数,自动确定调用那一个函数。这是通过函数重载的方式实现多态,这种实现的方式是静态的多态,因为在编译阶段就已经知道了的。

比如以下代码,通过重载Base类中的func方法,达到不同的参数产生不同的结果:

#include <iostream>

class Base {
public:
    Base(void){};
    ~Base(){};

    void func() { 
        std::cout << "Base func()" << std::endl;
    };
    void func(int a) {
        std::cout << "Base func(int a)" << std::endl;
    };
};

int main(void)
{
    Base base;
    base.func();      
    base.func(2);  

    return 0;
}

// 运行输出

Base func()
Base func(int a)

当然,也可以在子类中重载基类的函数方法,比如在基类Bash中派生出一个子类Subclass,重载func函数:

class Subclass : public Base {
public:
    Subclass(void) {};
    ~Subclass() {};

    using Base::func;       // 将基类的func所有重载实例到包含到Subclass中
    void func(std::string s) { 
        std::cout << "Subclass func(string s)" << std::endl;
    };
};

这样子在子类中也可以直接通过重载使用基类的函数:

#include <iostream>

class Base {
public:
    Base(void) {};
    ~Base() {};

    void func() { 
        std::cout << "Base func()" << std::endl;
    };
    void func(int a) {
        std::cout << "Base func(int a)" << std::endl;
    };
};

class Subclass : public Base {
public:
    Subclass(void) {};
    ~Subclass() {};

    using Base::func;
    void func(std::string s) { 
        std::cout << "Subclass func(string s)" << std::endl;
    };
};

int main(void)
{
    Base base;
    Subclass sub;
    
    sub.func();
    sub.func(2);
    sub.func("666");

    return 0;
}

// 运行结果
Base func()
Base func(int a)
Subclass func(string & s)

虚函数方式

当类中存在虚函数时,编译器会在类中自动生成一个虚函数表,虚函数表是一个存储类成员函数指针的数据结构,编译器会为对象自动生成一个指向虚函数表的指针(通常称之为 vptr 指针),它由编译器自动生成和维护,通过C++关键字 virtual 修饰的成员函数会被编译器放入虚函数表中。虚函数表是和类对应的,虚函数表指针是和对象对应的,而对象在运行过程中会被赋值或者是强转,因此这种实现的方式是动态的,因为虚函数表指针是可以被修改的,当父类中的虚函数表的指针被子类的虚函数表指针替换掉,那么在调用这个类的方法(虚函数)就会指向实际子类实现的方法,因此实现了多态。

那么如何定位虚函数表呢?编译器另外还为每个对象提供了一个虚函数表指针(即vptr),这个指针指向了对象所属类的虚函数表,在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向了所属类的虚函数表,从而在调用虚函数的时候,能够找到正确的函数。

正是由于每个对象调用的虚函数都是通过虚函数表指针来索引的,也就决定了虚函数表指针的正确初始化是非常重要的,换句话说,在虚函数表指针没有正确初始化之前,我们不能够去调用虚函数,那么虚函数表指针是在什么时候,或者什么地方初始化呢?这其实是在构造函数中进行虚函数表的创建和虚函数表指针的初始化,在构造子类对象时,要先调用父类的构造函数,此时编译器只“看到了”父类,并不知道后面是否还有继承者,它初始化父类对象的虚函数表指针,该虚函数表指针指向父类的虚函数表,当执行子类的构造函数时,子类对象的虚函数表指针被初始化,指向自身的虚函数表

总的来说:虚函数表可以继承,如果子类没有重写虚函数,那么子类虚函数表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现,如果基类有3个虚函数,那么基类的虚函数表中就有3项(虚函数地址),派生类也会有虚函数表,至少有3项,如果重写了相应的虚函数,那么虚函数表中的地址就会改变,指向自身的虚函数实现,如果派生类有自己的虚函数,那么虚函数表中就会添加该项。

比如以下的代码,创建一个父类,父类中包含一个虚函数Say(),同时再定义一个子类Son,子类中实现了Say()函数方法:

#include <iostream>

class Father {
public:
    Father(void) {};
    ~Father() {};
    
    virtual void Say() {
        std::cout << "father say hello!" << std::endl;
    };
};

class Son : public Father {
public:
    Son(void) {};
    ~Son() {};
    
    void Say(void) {
        std::cout << "son say hello!" << std::endl;
    };
};

int main(void)
{
    using namespace std;

    Father father;
    Son son;
    Father *father1 = &son; // 强制转换类型

    father.Say();
    son.Say();

    father1->Say();
}

// 运行结果

father say hello!
son say hello!
son say hello!

通过这个结果,我们发现是"son say hello",也就说它其实已经根据对象的类型调用了正确的函数,这就是虚函数方式实现了C++的多态性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入了解C++多态的原理及实现方式 的相关文章

  • 飞桨AIStudio基础操作

    目录 执行和调试 多文件代码编辑 上传Notebook Notebook快捷键 Notebook中使用Shell命令 查看文件夹目录 使用pip来安装自己需要的package 但不支持apt get 查看当前环境中安装的package 持久
  • VSCode与CMake搭配使用之基本配置

    1 首先安装VSCode插件CMake和CMake Tools CMake插件主要功能是CMake语法高亮 自动补全CMake Tools的功能主要是结合VSCode IDE使用CMake这个工具 xff0c 比如生成CMake项目 构建C
  • 九、设置元素等待

    转载于 xff1a http www testclass net selenium python element wait WebDriver提供了两种类型的等待 xff1a 显式等待和隐式等待 显式等待 显式等待使WebdDriver等待
  • Unity Tweak Tool使用

    这年头 xff0c 都讲究个性 Unity刚出来的时候 xff0c 就觉着新奇 xff0c 其他也没啥 xff0c 就是有些小不顺手 最开始使用的Ubuntu Tweak到后来的Gnome Tweak Tool都有点偏 xff0c ubun
  • MYSQL–my.cnf配置中文详解

    basedir 61 path 使用给定目录作为根目录 安装目录 character sets dir 61 path 给出存放着字符集的目录 datadir 61 path 从给定目录读取数据库文件 pid file 61 filenam
  • vscode中C/C++的Clang-format的使用

    一 介绍 Clang format是一个功能强大的格式化工具 在vs code通过C C 43 43 扩展后即可使用Clang format工具进行代码的格式化 其自带的排版格式主要有 xff1a LLVM Google Chromium
  • ShardingJDBC核心概念与快速实战

    目录 ShardingSphere介绍 ShardingSphere特点 ShardingSphere简述 ShardingSphere产品区分 ShardingJDBC实战 核心概念 实战 ShardingJDBC的分片算法 Shardi
  • 野火嵌入式学习笔记:第47讲 按键检测输入

    野火 第一期 Linux系列教学视频之 零基础入门 篇 xff0c 手把手教学 xff0c 从0开始 xff0c 基于野火i MX6ULL Pro MINI开发板 第47讲 检测按键输入 哔哩哔哩 bilibili 检测用户按键3 key按
  • 学习野火嵌入式笔记 第48讲:进程的由来

    野火 第二期 Linux系列教学视频之 内核编程 篇 xff0c 手把手教学 xff0c 硬件基于野火i MX6ULL Pro MINI开发板 野火 第二期 Linux系列教学视频之 内核编程 篇 xff0c 手把手教学 xff0c 硬件基
  • 学习野火嵌入式笔记:第68讲 I.MX^ULL 启动方式

    野火 第三期 Linux系列教学视频之 裸机开发 篇 xff0c 手把手教学 xff0c 基于野火i MX6ULL Pro MINI开发板 哔哩哔哩 bilibili 先了解其芯片手册以及linux开发板的原理图 全篇就在讲启动方式的引脚设
  • C语言 字符串-字符串的匹配

    字符串匹配函数 const char strstr const char str1 const char str2 搜索字符串在另一个字符串中首次出现的位置 const char p 61 strstr 34 how are you 34
  • Lambda表达式

    什么是Lambda表达式 Lambda 表达式是 Java8 新增的重要特性 xff0c Lambda 使 Java 具有了函数式编程的思想 xff0c 它的实质也是由编译器根据表达式推断最终生成原始语法的字节码方式 函数式编程思想是 强调
  • MPU6050多次发送数据,但第一次读取的数据为0

    实际现象并不是第一次数据为0 xff0c 一直读取数据 xff0c 中间也会出现数据为0的现象 xff0c 原因是 xff1a 跟采样频率有关 xff0c 如果采样频率为50Hz xff0c 那么读取数据间隔时间必须大于20ms xff0c
  • gazebo多机仿真时出现的问题解决方法

    多机仿真过程中出现的飞机不动的情况是 xff1a 下面几个步骤要严格执行 xff1a cd PX4 span class token operator span Autopilot git submodule update span cla
  • Linux c多线程与c++多线程的基础写法

    最近工作需要 xff0c 对多线程做了一点了解 xff0c 现在将多线程的创建 xff0c 函数传参做个小结 一 linux c多线程 1 头文件 xff1a include lt pthread h gt 2 创建线程 xff1a pth
  • 姿态误差问题

    前段同学问了一个关于飞机姿态误差的问题 xff0c 将飞机姿态直接做差与px4里面先z轴对齐然后将过渡矩阵的姿态量赋值给滚转俯仰姿态误差对比 xff0c 发现直接做差后在飞机做大的机动时误差量会变大 xff0c 曲线的拟合非常不好 xff0
  • px4 avoidance笔记

    最近在用px4官方的avoidance代码跑硬件避障 xff0c 官方介绍了只要生成符合sensor msgs PointCloud2点云信息就能使用 xff0c 因此为了应用长基线双目 xff0c 没有使用realsense的相机 xff
  • PX4源代码下载编译

    sudo git clone https github com PX4 PX4 Autopilot git recursivegit submodule update init recursivegit submodule update r
  • 踩坑记录

    ERROR Session line number was not unique in database History logging moved to new session 66 在生成tfrecord时 总报如下错误 找了很长时间
  • git强制覆盖本地代码

    git强制覆盖本地 xff1a git fetch all 拉取所有更新 git reset hard origin master 本地代码同步线上最新版本 会覆盖本地所有与远程仓库上同名的文件 git pull git强制覆盖本地命令 x

随机推荐

  • 【毕业设计】基于STM32及OpenMV的云台追踪装置

    目录 修改记录1 摘 要2 整体功能分析3 硬件选型3 1 OpenMV4 Cam H73 2 STM32F103ZET63 3 DS3120舵机3 4 LED补光板3 5 供电及稳压3 6 硬件连接 4 软件功能实现4 1 OpenMV部
  • firewall-cmd 使用总结

    firewalld的简要说明 firewalld firewall cmd firewall offline cmd它们Python脚本 xff0c 通过定义的在 usr lib firewalld下面的xml配置信息 xff0c 在启动时
  • 机架与机柜

    随着计算机网络的发展 xff0c 数据中心的服务器以及网络通信设备等IT设施 xff0c 正逐步向着网络化 机架化的方向发展 机架是用于综合布线 xff0c 安装配线架和理线架 xff0c 实现对电缆和光缆布线系统的管理 在网络机柜中不具备
  • cmake和makefile区别和cmake指定编译器(cmake -G)

    一 cmake和makefile区别 要说明区别 xff0c 我们先要区分下面三类工具 xff1a 1 项目构建生成工具 首先cmake是项目构建生成工具 xff0c cmake的代码可以与平台系统和编译器无关 类似cmake的工具还有au
  • Gazebo使用过程中的问题

    1 运行命令gazebo没有反应 在虚拟机安装好gazebo后 xff0c 使用ALT 43 F2打开命令行运行命令gazebo没有反应 首先尝试内存不够的情况 xff0c 给虚拟机加到了4g内存 没有用 xff0c 运行gazebo还是没
  • 一张图理解板卡硬件调试流程

    最近在调试从焊板厂打样回来的板卡 xff0c 简单总结了下板卡的硬件测试流程 xff0c 如下图 xff1a 写在后面的话 xff1a 我之所以选择做技术这一行 xff0c 是觉得做技术的人简单 直接 xff0c 当你面对一个技术问题 xf
  • 关于Linux进程你所需要知道的一切都在这里!!

    文章目录 进程简单了解进程查看进程进程ID父进程ID父进程与子进程 程序与进程程序进程程序变成进程总结 进程状态进程状态转换启动新进程system fork exce系列函数 终止进程等待进程wait waitpid 进程 说明 xff1a
  • MQTT协议简介及协议原理

    文章目录 MQTT协议MQTT协议简介MQTT通信模型MQTT客户端的功能 xff1a MQTT客户服务器功能 xff1a 消息主题与服务质量MQTT控制报文固定报头可变报头CONNECT报文CONNACK报文 有效载荷 MQTT协议 MQ
  • 你不得不看的图文并茂的MQTT协议通信过程!!!

    文章目录 MQTT连接服务器MQTT订阅主题MQTT发布消息服务质量等级 QoSQoS0的PUBLISH控制报文QoS1的PUBLISH控制报文QoS2的PUBLISH控制报文 取消订阅断开连接 MQTT连接服务器 客户端到服务器的网络连接
  • 一个高性能、高稳定性的跨平台MQTT客户端——mqttclient简介与使用

    文章目录 mqttclient简介与使用优势 xff1a 在线代码生成工具占用资源大小整体框架支持的平台版本问题版权和许可linux平台下测试使用安装cmake xff1a 测试程序编译 amp 运行编译成动态库libmqttclient
  • 一个高性能、高稳定性的跨平台MQTT客户端——mqttclient代码生产工具介绍

    文章目录 mqttclient代码生产工具介绍连接参数配置订阅主题相关的代码配置发布消息相关的代码配置生成代码 mqttclient代码生产工具介绍 mqttclient代码生产工具主要是用于配置MQTT的参数 xff0c 并且生成相应的代
  • 一个高性能、高稳定性的跨平台MQTT客户端——mqttclient配置及裁剪工具

    文章目录 mqttclient配置及裁剪工具salof相关的配置使用mqttclient裁剪配置工具 mqttclient配置及裁剪工具 MQTT TOPIC LEN MAX 配置客户端支持最大的主题名长度 xff0c 主题是支持通配符的
  • 一个高性能、高稳定性的跨平台MQTT客户端——mqttclient设计与实现方式

    文章目录 mqttclient设计与实现方式设计思想API接口MQTT客户端的核心结构mqttclient实现申请一个mqtt客户端释放已申请的mqtt客户端设置MQTT客户端的信息连接服务器订阅报文取消订阅发布报文内部线程核心的处理函数发
  • mqtt连接百度天工物接入平台

    文章目录 mqtt连接到百度天工物接入百度天工物接入简介使用百度天工物接入创建项目创建策略创建身份创建用户测试连接 MQTT软件测试连接手动安装相关的依赖包拉取mqttclient仓库简单介绍mqttclient仓库文件夹编译运行测试代码使
  • mqttclient连接到OneNET云平台

    文章目录 mqttclient连接到OneNET云平台使用OneNET测试连接手动安装相关的依赖包拉取mqttclient仓库简单介绍mqttclient仓库文件夹编译运行代码使用到的API mqttclient连接到OneNET云平台 有
  • 2022年最新《谷粒学院开发教程》:1 - 构建工程篇

    资料资料资料地址代码地址 后台管理系统目录前台展示系统目录1 构建工程篇7 渲染前台篇2 前后交互篇8 前台登录篇3 文件上传篇9 前台课程篇4 课程管理篇10 前台支付篇5 章节管理篇11 统计分析篇6 微服务治理12 项目完结篇 目录
  • MQTT移植到stm32开发板——使用RT-Thread操作系统

    文章目录 mqttclientENV介绍env工具下载安装通过env移植MQTT客户端打开 env 控制台打开env并更新软件包列表移植MQTT客户端 编写自己的代码 xff1a 连接参数配置订阅主题相关的代码配置发布消息相关的代码配置生成
  • MQTT移植到stm32开发板——使用TencentOS tiny操作系统

    mqttclient 一个高性能 高稳定性的跨平台MQTT客户端 一个高性能 高稳定性的跨平台MQTT客户端 xff0c 基于socket API之上开发 xff0c 可以在嵌入式设备 xff08 FreeRTOS LiteOS RT Th
  • MQTT移植到stm32开发板——使用FreeRTOS操作系统

    mqttclient 一个高性能 高稳定性的跨平台MQTT客户端 一个高性能 高稳定性的跨平台MQTT客户端 xff0c 基于socket API之上开发 xff0c 可以在嵌入式设备 xff08 FreeRTOS LiteOS RT Th
  • 深入了解C++多态的原理及实现方式

    文章目录 前言关于多态函数承载方式虚函数方式 前言 需要深入了解C C 43 43 语言的基础之上再看此文章 关于多态 具有多种形态 xff0c 调用同一个方法会随上下文不同而产生不同的结果 xff0c 多态有静态多态与动态多态两种 函数承