C++对象模型(10)-- 虚函数2

news2025/1/24 22:39:49

1、虚函数表、虚函数表指针的创建时机

我们知道虚函数表是属于类的,而虚函数表指针是属于对象的。在编译的时候,编译器会往类的构造函数中插入创建虚函数表指针的代码。同样,在编译期间编译器也为每个类确定好了对应的虚函数表的内容。

虚函数和普通函数一样,它们的地址在编译的时候就已经确定了。

当new一个含虚函数的类对象时,虚函数表、虚函数表指针、虚函数的内存布局是这样的:

2、不通过虚函数表指针的方式调用虚函数

虚函数和普通成员函数一样,地址在编译完成后就确定了。你可以通过虚函数表指针间接地调用虚函数,也可以通过对象直接调用虚函数。

(1)在成员函数中调用虚函数

在成员函数中调用虚函数跟调用普通函数一样,是不通过虚函数表的。

class Base {
public:
    virtual void vir_f() {
        std::cout << "Base::vir_f()" << std::endl;
    }
};

class Derive :public Base {
public:
            
    
    void testVirFun() {
        vir_f();
    }


};

int main()
{
    Derive derive;
    derive.testVirFun();

    return 0;
}

这时输出:Base::vir_f(),调用的是父类的虚函数。

在Derive类中添加虚函数vir_f():

void vir_f() {
    std::cout << "Derive::vir_f()" << std::endl;
}

这时输出:Derive::vir_f(),调用的是子类的虚函数。

(2)清除虚函数表指针后再调用虚函数

我们知道虚函数表指针是在构造函数中创建的,现在假如我们在构造函数中把虚函数表指针去掉,看看这时虚函数能否被调用。

class Base {
public:
    int b_i = 8;
    // 在构造函数中用memset把虚函数表指针去掉
    Base() {
        memset(this, 0, sizeof(Base));
    }

    virtual void vir_f() {
        std::cout << "Base::vir_f()" << std::endl;
    }

    virtual void vir_g() {
        std::cout << "Base::vir_g()" << std::endl;
    }

    virtual void vir_h() {
        std::cout << "Base::vir_h()" << std::endl;
    }
};

这时,我们在main()函数中加入如下代码:

int main()
{

    Base* pb = new Base();
    

    pb->vir_f();
}

发现运行报错。因为在Base的构造函数中,我们用memset()把虚函数表指针给清除掉了。

现在我们把main()函数修改一下,改成下面这样。

int main()
{
    Base base;
    base.vir_f();
}

发现虚函数vir_f()能够被正确调用。

3、vcall

使用函数指针调用成员虚函数的时候会使用到vcall,通过vcall能从虚函数表中找到被调用的虚函数地址。

我们可以通过代码来验证这个vcall。

class Base {
public:
    virtual void vir_f() {
        std::cout << "Base::vir_f()" << std::endl;
    }

    virtual void vir_g() {
        std::cout << "Base::vir_g()" << std::endl;
    }
};

int main()
{
    printf(" Base::vir_f的地址:%p\n", &Base::vir_f);
    printf(" Base::vir_g的地址:%p\n", &Base::vir_g);

    Base* pb = new Base;
    pb->vir_g();

    std::cout << "pause" << std::endl;
    return 0;
}

把断点设在这行:std::cout

从运行结果可以看到,虚函数的地址分别是:00f111c2、00f11104,根打印的地址不同。

把断点设在printf这行,然后再运行,切换到反汇编窗口。

可以看到vcall{0}, (0F1146Ah)和vcall{4}, (0F110D2h)这样的内容。

其中,vcall{0}对应虚函数vir_f(),vcall{4}对应虚函数vir_g()。我们可以这么认为,在调用虚函数时系统先是找到vcall,然后再通过vcall找到真正需要调用的虚函数。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

巡检系统是什么?设备巡检系统有什么用?

在现今这个高度自动化的时代&#xff0c;许多企业的设备规模日益扩大&#xff0c;设备巡检工作也变得越来越重要。它不仅是保证企业设备正常运行的重要环节&#xff0c;也是维护生产安全和提升运营效率的关键。那么&#xff0c;如何有效地进行设备巡检呢&#xff1f;答案就是—…

CSS Vue/RN 背景使用opacity,文字在背景上显示

Vue <div class"training_project_tip"> <div class"tip">展示的文字</div> </div> .training_project_tip { font-size: 12px; font-weight: 400; text-align: left; color: #ffffff; margin-top: 8px; position: relative; dis…

6.自定义相机控制器

愿你出走半生,归来仍是少年&#xff01; Cesium For Unity自带的Dynamic Camera,拥有优秀的动态展示效果&#xff0c;但是其对于场景的交互方式用起来不是很舒服。 通过模仿Cesium JS 的交互方式&#xff0c;实现在Unity中的交互&#xff1a; 通过鼠标左键拖拽实现场景平移通过…

模式植物背景基因集制作

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 写在前面 关于GO背景基因集文件的制作&#xff0c;我们在很早以前也发过。近两天&#xff0c;自己在分析时候&#xff0c;也是被搞了头疼。想重新制作一份GO背景基因集&#xff0c;进行富集分析。但是结果&…

Cpolar+Inis结合在Ubuntu上打造出色的博客网站,快速上线公网访问

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

编辑器功能:用一个快捷键来【锁定】或【解开】Inspector面板

一、需求 我有一个脚本&#xff0c;上面暴露了许多参数&#xff0c;我要在场景中拖物体给它进行配置。 如果不锁定Inspector面板的话&#xff0c;每次点击物体后&#xff0c;Inspector的内容就是刚点击的物体的内容&#xff0c;而不是挂载脚本的参数面板。 二、 解决 &…

适老化改造监管平台

文章目录 前言一、首页二、客户管理三、评估管理四、施工管理五、验收管理总结 前言 适老化改造监管平台是指一种为老年人住房进行适老化改造所建立的监管平台。该平台可以辅助政府监管相关企业或个人为老年人住房进行适老化改造的工作&#xff0c;确保改造符合相关标准和规定…

Android--Retrofit2执行多个请求任务并行,任务结束后执行统一输出结果

场景&#xff1a;后端上传文件接口只支持单个文件上传&#xff0c;而业务需求一次性上传多个图片&#xff0c;因此需要多个上传任务并发进行&#xff0c;拿到所有的返回结果后&#xff0c;才能进行下一个流程。 1、使用Java并发工具 private List<Response<JSONObject>…

JVM三色标记

三色标记 什么是三色标记法 三色标记法&#xff0c;也被称为Tri-color Marking Algorithm&#xff0c;是一种用于追踪对象存活状态的垃圾回收算法。它基于William D. Hana和Mark S. McCulleghan在1976年提出的两色标记法的基础上进行了改进。 与两色标记法只能将对象标记为“…

c++ --- 归并排序

2、归并排序 步骤&#xff1a; 选取中间点 mid (LR)/2递归排序 左边left 和 右边 right归并 ---- 将数组合二为一 模板代码 int temp[10]; void merge_sort(int q[], int l, int r) {//如果左右边界相等 直接退出if (l > r) return;//获取数组中心int mid (l r) / 2;/…

如何将模型原点设置到模型的中心

1、为什么要调整坐标原点位置&#xff1f; 从事3D建模相关工作的朋友们在工作中经常会需要调整模型的坐标原点&#xff0c;那么为什么一定要调整模型的坐标原点呢&#xff1f;主要原因如下&#xff1a; 方便后续操作&#xff1a;将原点设置为几何中心可以方便后续对模型进行旋…

香港主机免备案吗?为什么不用备案?

​  对于许多人来说&#xff0c;选择一个合适的主机是建立网站的重要一步。而在选择主机时&#xff0c;备案问题往往成为了一个让人头疼的难题。有幸的是&#xff0c;香港主机免备案&#xff0c;成为了不少网站建设者的首选。 那么&#xff0c;为什么香港主机不需要备案呢?我…

会议OA小程序首页布局

目录 一. Flex布局介绍 1.1 什么是Flex布局 1.2 基本概念 1.3 Flex属性 二. 会议OA首页轮播图的实现 配置 Mock工具 swiper 效果展示 三. 会议OA首页会议信息布局 index.js index.wxml index.wxss 首页整体效果展示 一. Flex布局介绍 布局的传统解决方案&#x…

LeetCode09——回文数

LeetCode09 自己写的解,转化为字符串再反转&#xff0c;比较笨。 import java.util.Scanner; public class Result01 {public static void main(String[] args) {System.out.println("请输入整数&#xff0c;我来帮您判断是否是回文数。");Scanner scanner new Sc…

2024年孝感市建筑类中级职称申报资料私企VS国企

2024年孝感市建筑类中级职称申报资料私企VS国企 民营企业中级职称申报跟事业单位或者是国企申报中级职称流程不一样么&#xff1f;实际上流程基本都是相同的&#xff0c;就是提交纸质版资料有点不一样。 孝感市建筑类中级职称申报基本流程 1.参加建筑类中级职称水平能力测试。 …

playwright: local variable ‘page‘ referenced before assignment

安装好playwright后&#xff0c;运行相关程序出现此错误&#xff0c;按照下述链接中的方法安装相关组件和浏览器驱动后&#xff0c;问题得以解决。 https://www.cnblogs.com/fengyangsheng/p/17531254.html安装playwright pip install -i https://mirrors.aliyun.com/pypi/si…

“视频剪辑:用马赛克巧妙遮盖水印,让你的视频更完美

想象一下&#xff0c;你正在欣赏一个精彩纷呈的视频&#xff0c;突然间&#xff0c;一个不和谐的水印闯入视线&#xff0c;打破了画面的和谐。是不是瞬间影响了你的观影体验&#xff1f;那么&#xff0c;如何巧妙地解决这个问题呢&#xff1f;今天&#xff0c;我们就来探讨一下…

JavaSE入门---认识运算符

文章目录 算术运算符关系运算符逻辑运算符位运算符移位运算符条件运算符运算符的优先级 计算机的最基本的用途之一就是执行数学运算&#xff0c;运算过程中就会用到运算符&#xff0c;那什么是运算符呢&#xff1f; 即&#xff1a;对操作数进行操作的符号&#xff0c;不同运算符…

《小狗钱钱》阅读笔记(三)

目录 还会有各种各样的人取笑你&#xff0c;但也会有更多的人认可你 有的时候&#xff0c;疯狂的念头比普通的小目标更容易达到。当你定下大目标的时候&#xff0c;就意味着你必须付出比别人多得多的努力。 可是请你告诉我&#xff0c;你为什么不能因为做了一件自己喜欢的事…

功夫猫小游戏

欢迎来到程序小院 功夫猫 玩法&#xff1a; 对准对方猫点击鼠标左键进行扑街&#xff0c;碰到敌方猫扑街X1&#xff0c;不要让对方猫碰到自己&#xff0c;统计扑街次数&#xff0c;快去玩功夫猫吧^^。开始游戏https://www.ormcc.com/play/gameStart/189 html <canvas id&q…