调用基类的纯虚函数,如何知道纯虚函数会调用哪个派生类(子类)中的实现。
在 C++ 中,调用基类的纯虚函数实际上是通过运行时多态性来决定调用哪一个派生类的实现。这种机制是通过虚函数表(vtable)和虚函数指针(vptr)实现的。下面我们来详细探讨一下这个过程。
虚函数表和虚函数指针
-
虚函数表(vtable):
- 每个包含虚函数的类(包括纯虚函数)都会有一个虚函数表。虚函数表是一个指针数组,每个指针指向类的虚函数的具体实现。
- 虚函数表是编译器在编译时生成的,并且对于同一个类的所有对象是共享的。
-
虚函数指针(vptr):
- 每个对象有一个指向其类的虚函数表的指针,称为虚函数指针(vptr)。
- 当一个对象被创建时,其 vptr 被初始化为指向该对象所属类的虚函数表。
当调用一个虚函数时,程序会通过对象的 vptr 找到相应的 vtable,并在 vtable 中找到该函数的地址,然后进行调用。这种机制允许程序在运行时根据对象的实际类型调用适当的函数实现,这就是多态性。
调用纯虚函数的过程
假设你有一个基类 Base 和几个派生类 Derived1 和 Derived2,基类 Base 定义了一个纯虚函数 doSomething。以下是如何知道调用哪个派生类实现的步骤:
对于 obj2->doSomething(),类似的过程会发生,但它的 vptr 指向 Derived2 的 vtable,最终调用 Derived2 中 doSomething 的实现。
运行时确定派生类的实现
这是因为 C++ 的多态性允许基类指针(或引用)指向派生类对象。调用虚函数时,实际调用的函数实现是通过对象的动态类型(即它真正的派生类类型)来确定的。这种类型是在运行时决定的,而不是编译时。
代码示例
下面是一个完整的代码示例,展示了上述过程:
-
定义类和函数:
class Base { public:virtual void doSomething() = 0; // 纯虚函数 };class Derived1 : public Base { public:void doSomething() override {std::cout << "Derived1 implementation" << std::endl;} };class Derived2 : public Base { public:void doSomething() override {std::cout << "Derived2 implementation" << std::endl;} }; -
实例化派生类对象:
Base* obj1 = new Derived1(); Base* obj2 = new Derived2(); -
调用虚函数:
obj1->doSomething(); // 调用 Derived1 的实现 obj2->doSomething(); // 调用 Derived2 的实现
决定调用哪个派生类实现的过程
当你调用 obj1->doSomething() 时,以下过程发生:
-
查找 vptr:
obj1是指向Derived1对象的基类指针。- 程序通过
obj1找到它的 vptr,该 vptr 指向Derived1的 vtable。
-
查找 vtable:
- 程序查找
Derived1的 vtable,这个表包含doSomething的地址。
- 程序查找
-
调用函数:
- 程序通过 vtable 获取
doSomething的地址,然后调用这个地址处的函数,即Derived1中doSomething的实现。
- 程序通过 vtable 获取
对于 obj2->doSomething(),类似的过程会发生,但它的 vptr 指向 Derived2 的 vtable,最终调用 Derived2 中 doSomething 的实现。
运行时确定派生类的实现
这是因为 C++ 的多态性允许基类指针(或引用)指向派生类对象。调用虚函数时,实际调用的函数实现是通过对象的动态类型(即它真正的派生类类型)来确定的。这种类型是在运行时决定的,而不是编译时。
代码示例
下面是一个完整的代码示例,展示了上述过程:
#include <iostream>class Base {
public:virtual void doSomething() = 0; // 纯虚函数
};class Derived1 : public Base {
public:void doSomething() override {std::cout << "Derived1 implementation" << std::endl;}
};class Derived2 : public Base {
public:void doSomething() override {std::cout << "Derived2 implementation" << std::endl;}
};int main() {Base* obj1 = new Derived1();Base* obj2 = new Derived2();obj1->doSomething(); // 输出: Derived1 implementationobj2->doSomething(); // 输出: Derived2 implementationdelete obj1;delete obj2;return 0;
}
在这个示例中,通过基类指针调用 doSomething 时,程序根据实际的派生类类型调用相应的实现,这展示了 C++ 中的运行时多态性。
通过调试查看
如果你使用调试器(如 gdb),你可以在调用虚函数前设置断点,并逐步查看调用过程。你会看到程序通过 vptr 查找 vtable,然后调用适当的函数实现。这是验证多态行为的一个好方法。
总结
- vptr 和 vtable: vptr 指向对象的 vtable,通过它们在运行时决定调用哪个派生类的实现。
- 多态性: 基类指针或引用调用虚函数时,实际调用的是派生类的实现,这通过动态绑定实现。
- 调试和分析: 使用调试器可以更深入地观察这种运行时行为。
相关文章:
调用基类的纯虚函数,如何知道纯虚函数会调用哪个派生类(子类)中的实现。
在 C 中,调用基类的纯虚函数实际上是通过运行时多态性来决定调用哪一个派生类的实现。这种机制是通过虚函数表(vtable)和虚函数指针(vptr)实现的。下面我们来详细探讨一下这个过程。 虚函数表和虚函数指针 虚函数表&a…...
塑造卓越企业家IP:多维度视角下的策略解析
在构建和塑造企业家IP的过程中,我们需要从多个维度进行考量,以确保个人品牌能够全面、立体地展现企业家的独特魅力和价值。以下是从不同角度探讨如何做好一个企业家IP的策略。 一、从个人特质出发 深入了解自我:企业家需要清晰地认识到自己的…...
Rust 跨平台-Android 和鸿蒙 OS
1. 安装 rustup rustup 是 Rust 的安装和版本管理工具 $ curl --proto https --tlsv1.2 https://sh.rustup.rs -sSf | sh 该命令会安装 rusup 和最新的稳定版本的 Rust;包括: rustc Rust 编译器,用于将 Rust 代码编译成可执行文件或库。 ca…...
Typora导出为Word
文章目录 一、场景二、安装1、网址2、解压并验证 三、配置四、重启Typora 一、场景 在使用Typora软件编辑文档时,我们可能需要将其导出为Word格式文件 当然我们可以直接在菜单里进行导出操作 文件-> 导出-> Word(.docx) 如果是第一次导出word文件࿰…...
数据库被后台爆破如何解决?
在数字化时代,数据库安全成为企业与组织不容忽视的关键环节。其中,“后台爆破”攻击,即通过自动化工具尝试大量的用户名和密码组合,以非法获取数据库访问权限,是常见的安全威胁之一。本文将详细介绍如何识别、防御并解…...
php7.4源码安装dbase7.1.1扩展
安装PHP开发工具 首先,你需要安装PHP开发工具,包括php-devel(或php7.4-devel,取决于你的PHP版本)和其他编译工具。 bash sudo yum install php7.4-devel gcc make 注意:如果你使用的是不同的PHP版本&#…...
OkHttp的源码解读1
介绍 OkHttp 是 Square 公司开源的一款高效的 HTTP 客户端,用于与服务器进行 HTTP 请求和响应。它具有高效的连接池、透明的 GZIP 压缩和响应缓存等功能,是 Android 开发中广泛使用的网络库。 本文将详细解读 OkHttp 的源码,包括其主要组件…...
08:结构体
结构体 1、为什么需要结构体2、如何定义结构体3、怎么使用结构体变量3.1、赋值和初始化3.2、结构体变量的输出 1、为什么需要结构体 为了表示一些复杂的事物,而普通的基本类型无法满足实际要求。什么叫结构体 把一些基本类型数据组合在一起形成的一个新的数据类型&…...
喜讯!安全狗荣获“2023年网络安全技术支撑优秀单位”称号
6月6日,由中共厦门市委网络安全和信息化委员会办公室(以下简称“厦门市委网信办”)主办的2023年网络安全技术支撑优秀单位颁奖仪式在厦门成功举行。 作为国内云原生安全领导厂商,安全狗受邀出席此次活动。 会上,安全狗…...
android里面json操作
1.读取assets下面xzhd/aikit/pck.json String json = null; try { InputStream is = activity.getAssets().open(aikitPathInData+"xzhd/aikit/pck.json"); int size = is.available(); byte[] buffer = new byte…...
MATLAB的.m文件与Python的.py文件:比较与互参
simulink MATLAB的.m文件与Python的.py文件:比较与互参相似之处**1. 基本结构****2. 执行逻辑****3. 可读性和维护性** 差异性**1. 语法特性****2. 性能和应用****3. 开发环境** 互相学习的可能性结论 MATLAB的.m文件与Python的.py文件:比较与互参 在编…...
武汉星起航:自运营团队精准把握亚马逊红利,引领跨境电商新潮流
在全球化的浪潮下,跨境电商行业蓬勃发展,为众多企业带来了前所未有的机遇。武汉星起航电子商务有限公司便是其中的佼佼者,其自运营团队凭借对亚马逊平台的深入了解和丰富的运营经验,成功抓住了亚马逊的流量红利,为公司…...
嵌入式计算器模块实现
嵌入式计算器模块规划 计算器混合算法解析 上面我们的算法理论已经完善, 我们只用给一个混合运算式, 计算器就可以帮助我们计算出结果. 但是存在一个痛点, 每次计算算式,都要重新编译程序, 所以我们想到了, 利用单片机, 读取用户输入的按键, 组成算式, 输入给机器, 这样我们就…...
tomcat定时重启
Tomcat定时重启(linux) 1. 编写脚本 在tomcat的bin目录下,使用vim restart.sh,编写restart.sh脚本,插入一下内容,最后并保存! #!/bin/bash# 初始化全局环境变量 . /etc/profilecd /usr/loca…...
构建LangChain应用程序的示例代码:48、如何使用非文本生成工具创建多模态代理
多模态输出:图像和文本 这个示例展示了如何使用非文本生成工具来创建多模态代理。 本例仅限于文本和图像输出,并使用UUID在工具和代理之间传输内容。 本例使用Steamship生成和存储生成的图像。生成的内容默认受到身份验证保护。 您可以在这里获取Ste…...
【笔记】记录一次全新的Java项目部署过程
记录一次全新的Java项目部署过程 环境:CentOS7一、初始环境准备 yum install wget -y yum install vim -y yum install net-tools -y mkdir /data mkdir /data/html mkdir /data/backend一、安装JDK 17 安装JDK17# 下载rpm wget https://download.oracle.com/java/17/latest/…...
达梦数据库系列—14. 表空间的备份和还原
目录 1、表空间备份 2、表空间还原 3、表空间恢复 4、增量还原恢复 1、表空间备份 表空间只能在联机状态下进行备份。 BACKUP TABLESPACE TBS BACKUPSET /dm/backup/dm_bak/ts_bak_01; 完全备份 BACKUP TABLESPACE TBS FULL BACKUPSET /dm/backup/dm_bak/ts_full_bak_01…...
奔驰G350升级原厂自适应悬挂系统有哪些作用
奔驰 G350 升级自适应悬挂系统后,可根据行车路况自动调整悬架高度和弹性,从而提升驾乘的舒适性和稳定性。 这套系统的具体功能包括: • 多种模式选择:一般有舒适、弯道、运动及越野等模式。例如,弯道模式在过弯时能为…...
一个启动脚本例子
一、全部代码 #!/bin/bash DATE$(date %Y%m%d)SOURCE"abc.jar" TARGET"backup/abc.jar.jew.$DATE"if [ -f "$SOURCE" ]; thencp "$SOURCE" "$TARGET" firm -f abc.jar mv abc_1.jar abc.jarpidNumps -ef | grep $SOURCE |…...
grpc学习golang版( 六、服务器流式传输 )
系列文章目录 第一章 grpc基本概念与安装 第二章 grpc入门示例 第三章 proto文件数据类型 第四章 多服务示例 第五章 多proto文件示例 第六章 服务器流式传输 第七章 客户端流式传输 第八章 双向流示例 文章目录 一、前言二、定义proto文件三、拷贝任意文件进项目四、编写serve…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
CSS3相关知识点
CSS3相关知识点 CSS3私有前缀私有前缀私有前缀存在的意义常见浏览器的私有前缀 CSS3基本语法CSS3 新增长度单位CSS3 新增颜色设置方式CSS3 新增选择器CSS3 新增盒模型相关属性box-sizing 怪异盒模型resize调整盒子大小box-shadow 盒子阴影opacity 不透明度 CSS3 新增背景属性ba…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
