调用基类的纯虚函数,如何知道纯虚函数会调用哪个派生类(子类)中的实现。
在 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…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
