c++虚函数表中的内存布局

news2024/11/17 16:18:34

c++虚函数表中的内存布局

  • 1.Class的内存分布
  • 2.其他修改Class中变量的方法
  • 3.通过虚函数表内存偏移调用虚函数
  • 4.继承状态下的虚函数表内存
  • 5.派生类函数中多出来的虚函数访问("基类指针指向子类对象")

1.Class的内存分布

#include <iostream>

using namespace std;

#define Debug  cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl

class Base
{

};

int main()
{
    cout<<sizeof(Base)<<endl;//1

    return 0;
}
#include <iostream>

using namespace std;

#define Debug  cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl

class Base
{
private:
    int _val;
    int static _staticval;//说明静态成员变量不属于类

public:
    int Getval() const//说明成员函数也不属于类
    {
        return _val;
    }
};

int main()
{
    cout<<sizeof(Base)<<endl;//4

    return 0;
}
#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{
private:
    int _val;
    int static _staticval; // 说明静态成员变量不属于类

public:
    int Getval() const // 说明成员函数也不属于类
    {
        return _val;
    }
};

class Base_A : public Base
{
};

int main()
{
    cout << sizeof(Base_A) << endl; // 4
    printf("%p\n%p\n", &Base::Getval, &Base_A::Getval);
    /*
    00007FF6D2AC1131
    00007FF6D2AC1131   //不重载的话,从派生类继承下来的函数是同一个函数,也就是说"函数的内存不在类里面"
    */

    return 0;
}
#include <iostream>

using namespace std;

#define Debug  cout<<'<'<<__FILE__<<">,"<<__FUNCTION__<<","<<__LINE__<<endl

class Base
{
private:
    int _val;
    int static _staticval;//说明静态成员变量不属于类

public:
    int Getval() const//说明成员函数也不属于类
    {
        return _val;
    }
};

class Base_A : public Base
{
public:
    int Getval() const
    {
        return 0;
    }
};

int main()
{
    cout<<sizeof(Base_A)<<endl;//4
    printf("%p\n%p\n",&Base::Getval,&Base_A::Getval);
    /*
    000000000061fdf0
    000000000061fde0   //重载了所以地址才发生了变化
    */

    return 0;
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.其他修改Class中变量的方法

在这里插入图片描述
类这种概念只在编译的时候存在

#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{
private:
    int _val;
    int _valB;
public:
    int GetVal() const
    {
        return _val;
    }
    int GetValB() const
    {
        return _valB;
    }
};



int main()
{
    Base obj;
    int* objPtr = (int*)&obj;
    *objPtr = 999;

    *(objPtr + 1) = 1000;

    cout << obj.GetVal() << endl;//999
    cout << obj.GetValB() << endl;//1000
    

    return 0;
}
#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    Base obj;
    U8* objAddre = (U8*)&obj;//这个类
    U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组

    ((FuncPtr)objArr[0]) ();
    ((FuncPtr)objArr[1]) ();
    ((FuncPtr)objArr[2]) ();

    /*输出
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
    */

    return 0;
}

3.通过虚函数表内存偏移调用虚函数

#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    Base obj;
    U8* objAddre = (U8*)&obj;//这个类
    U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组

    ((FuncPtr)objArr[0]) ();
    ((FuncPtr)objArr[1]) ();
    ((FuncPtr)objArr[2]) ();

    /*输出
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
    */

    return 0;
}
#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    {
        Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();
    }
    {
        Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组

        printf("%p\n",objArr);
    }

    /*输出
    00007FF63F7BBD30
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
    <D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
    00007FF63F7BBD30///地址一样:说明:同一个类 实例化多个对象 虚函数表指针是一样的
    */

    return 0;
}

在这里插入图片描述

4.继承状态下的虚函数表内存

  • 1.没有重写,虚函数表中的元素地址是一样的
  • 2.重写后,对应的虚函数表中的函数指针就是新的地址
#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

class A_Base : public Base
{
    virtual void VirB()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    {
        A_Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        //printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();
    }
 

    /*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
    */

    return 0;
}

在这里插入图片描述

#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

class A_Base : public Base
{
    virtual void VirB()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    {
        Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        //printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();

        for(int i = 0 ; i < 3 ; i++)
            printf("%p\n", objArr[i]);
    }
    printf(".........................................................\n");
    {
        A_Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        //printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();

        for (int i = 0; i < 3; i++)
            printf("%p\n", objArr[i]);
    }
 

    /*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF7979B1442
00007FF7979B115E
00007FF7979B1474
.........................................................
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF7979B1442
00007FF7979B14B0
00007FF7979B1474
    */

    return 0;
}

在这里插入图片描述

5.派生类函数中多出来的虚函数访问(“基类指针指向子类对象”)

“基类指针指向子类对象"或"基类指针指向派生类对象”

#include <iostream>

using namespace std;

#define Debug cout << '<' << __FILE__ << ">," << __FUNCTION__ << "," << __LINE__ << endl

class Base
{

public:
    virtual void VirA()
    {
        Debug;
    }
    virtual void VirB()
    {
        Debug;
    }
    virtual void VirC()
    {
        Debug;
    }
};

class A_Base : public Base
{
    virtual void VirB()
    {
        Debug;
    }

    virtual void VirD()
    {
        Debug;
    }
};

using U8 = long long;
using FuncPtr = void(*)();//函数指针
int main()
{
    {
        Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        //printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();

        for (int i = 0; i < 3; i++)
            printf("%p\n", objArr[i]);
    }
    printf(".........................................................\n");
    {
        A_Base obj;
        U8* objAddre = (U8*)&obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        //printf("%p\n", objArr);
        ((FuncPtr)objArr[0]) ();
        ((FuncPtr)objArr[1]) ();
        ((FuncPtr)objArr[2]) ();

        for (int i = 0; i < 3; i++)
            printf("%p\n", objArr[i]);
    }

    {
        Base* obj = new A_Base;
        //obj->VirD();//报错:常规的方法:基类指针 无法访问 派生类函数中多出来的虚函数
        U8* objAddre = (U8*)obj;//这个类
        U8* objArr = (U8*)(*objAddre);//解引用,这时候,就是那个虚函数的数组
        ((FuncPtr)objArr[3]) ();
    }


    /*输出
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirB,17
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF71EBE14CE
00007FF71EBE1181
00007FF71EBE150A
.........................................................
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirA,13
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirB,29
<D:\vs2022\code\测试cpp语法\main.cpp>,Base::VirC,21
00007FF71EBE14CE
00007FF71EBE10A5
00007FF71EBE150A
<D:\vs2022\code\测试cpp语法\main.cpp>,A_Base::VirD,34
    */

    return 0;
}

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

ThreadLocal核心源码阅读

1. 概述 ThreadLocal为每个使用该变量的线程提供独立的变量副本&#xff0c;因此每一个线程都可以独立地改变自己的副本&#xff0c;而不会影响其他线程。 入门例子&#xff1a; public class ThreadLocalStudy {static ThreadLocal<String> stringThreadLocal new T…

Python爬取公众号封面图(零基础也能看懂)

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

刷题之Leetcode209题(超级详细)

209.长度最小的子数组 力扣题目链接(opens new window)https://leetcode.cn/problems/minimum-size-subarray-sum/ 给定一个含有 n 个正整数的数组和一个正整数 s &#xff0c;找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&#xff0c;并返回其长度。如果不存在符合条…

地面站Mission Planner从源码编译与运行

0. 环境 - win10&#xff08;基本需要100G硬盘&#xff09; - ubuntu18 1. 安装vs2022 下载 vs2022 community 在线安装包。 https://visualstudio.microsoft.com/ 打开 Visual Studio Installer 先安装 Visual Studio Community 2022本体。占用1.2GB。 Visual Studio Inst…

树状数组相关题目

题目一 方法一 归并分治 代码&#xff1a; # include <stdio.h>int arr[100]; int help[100];int n;//归并分治 // 1.统计i、j来自 l~r 范围的情况下&#xff0c;逆序对数量 // 2.统计完成后&#xff0c;让arr[l...r]变成有序的 int f(int l, int r) {if (l r)return…

html写一个登录注册页面

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册登录界面Ⅰ</title><link rel"stylesheet" href"https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.mi…

CSS属性计算逻辑

CSS 属性计算逻辑 首先&#xff0c;假设在 HTML 中有这么一段代码&#xff0c;在 body 中有一个 h1 标题&#xff1a; <body><h1>这是一个h1标题</h1> </body>目前我们没有设置该 h1 的任何样式&#xff0c;但是却能看到该 h1 有一定的默认样式&…

PHP实现网站微信扫码关注公众号后自动注册登陆实现方法及代码【关注收藏】

在网站注册登陆这环节&#xff0c;增加微信扫码注册登陆&#xff0c;普通的方法需要开通微信开发者平台&#xff0c;生成二维码扫码后才能获取用户的uinonid或openid&#xff0c;实现注册登陆&#xff0c;但这样比较麻烦还要企业认证交费开发者平台&#xff0c;而且没有和公众号…

Linux:五种IO模型的基本认识

文章目录 IO的本质五种IO模型异步和同步 阻塞IO非阻塞IO信号驱动IO IO的本质 在之前的内容中已经结束了对于网络和操作系统的学习&#xff0c;那么回过来再继续看IO&#xff0c;什么是IO呢&#xff1f; 对于网络的学习当中&#xff0c;实际上也是一种IO&#xff0c;数据从计算…

基于单片机钢琴电子节拍器系统设计

**单片机设计介绍&#xff0c;基于单片机钢琴电子节拍器系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机钢琴电子节拍器系统设计是一个综合性的项目&#xff0c;它结合了单片机编程、音频处理、用户界面设计等多个领域的…

Ai软件下载安装 Adobe Illustrator一键安装 5分钟搞定

ai软件又叫adobe illustrator软件,它是设计者们比较常用的平面设计软件,不仅可以用来学习日常的学习设计,还能够用作于商业设计用途,作为一款全球知名的矢量图形处理工具,ai软件广泛应用于印刷出版、海报书籍排版、专业插画、多媒体图像处理和互联网页面的制作等.从事设计行业的…

Ceph学习 - 2.分布式文件系统DFS

文章目录 1.分布式文件系统DFS1.1 DFS简介1.1.1 存储基础1.1.2 分布式文件系统1.1.3 DSS简介1.1.4 常见的文件系统 1.2 原理解读1.2.1 分布式数据存储1.2.2 存储角色1.2.3 数据高可用 1.3 小结 1.分布式文件系统DFS 学习目标&#xff1a;这一节&#xff0c;我们从DFS简介、原理…

《MATLAB科研绘图与学术图表绘制从入门到精通》

解锁MATLAB科研绘图魅力&#xff0c;让数据可视化成为你的科研利器&#xff01; 1.零基础快速入门&#xff1a;软件操作实战案例图文、代码结合讲解&#xff0c;从入门到精通快速高效。 2.多种科研绘图方法&#xff1a;科研绘图基础变量图形极坐标图形3D图形地理信息可视化等&a…

JavaWeb前端基础(HTML CSS JavaScript)

本文用于检验学习效果&#xff0c;忘记知识就去文末的链接复习 1. HTML 1.1 HTML基础 结构 头<head>身体<body> 内容 图片<img>段落<p>图标<link> 标签 单标签双标签 常用标签 div&#xff1a;分割块span&#xff1a;只占需要的大小p&…

Linux网卡IP地址配置错误的影响

在Linux系统中&#xff0c;网络配置是保持系统顺畅运行的关键一环。正确配置网卡的IP地址对于确保网络通信的准确性和效率至关重要。然而&#xff0c;如果在这个过程中发生错误&#xff0c;可能会带来一系列问题。让我们一起探讨一下&#xff0c;如果Linux网卡的IP地址配置错误…

nVisual软件在企业运维管理中的价值

​ 企业运维常见问题 1. 设备进出机房&#xff0c;上架下架一直使用excel表格记录&#xff0c;无法持续跟踪机柜设备变化&#xff0c;对机房内设备管理无法做到全过程记录&#xff1b; 2. 资料分散缺乏统一管理&#xff0c;表格手工记录容易产生遗漏&#xff0c;资料不准确&a…

小核引导RTOS---RISC-V C906

文章目录 参考日志编译框架目标fip 启动流程fip文件组成BL2程序 总结思考备注 参考 参考1. How does FSBL load the FreeRTOS on the small core and execute it?参考2. Duo now supports big and little cores?Come and play!Milk-V Duo, start&#xff01;参考3. 使用uboo…

【御控物联】JavaScript JSON结构转换(16):对象To数组——综合应用

文章目录 一、JSON结构转换是什么&#xff1f;二、术语解释三、案例之《JSON对象 To JSON数组》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么&#xff1f; JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0…

【JavaSE零基础】00-基础语法(1-12章)

1 第一章 Java开发环境搭建 1.1 章节目标与知识框架 1.1.1 章节目标 掌握Java的开发环境搭建&#xff0c;会编写HelloWorld程序&#xff0c;并能够准确的进行编译和运行&#xff1b;理解path和classpath环境变量并可以自行配置。 1.1.2 知识框架 1.2 Java语言概述(了解) J…

20240404这个数字有什么特点吗?

今天是2024年的清明节&#xff0c;20240404这个数字让我提出了一个疑问&#xff0c;它是否有什么含义或者特点呢&#xff1f; 首先&#xff0c;如果把它拆分为两个整数的平方和&#xff0c;会怎么样呢&#xff1f; 于是&#xff0c;我一顿操作猛如虎&#xff0c;搞出了这么个…