C++类的内存地址存放问题

2023-05-16

了解C++类地址的存放和分配等问题,能帮助我们更深入、更清晰了解类的组成及其使用。

自己目前不是很清楚,先收集一些网上资料,而后再慢慢补充增加的了解...

//-------------------------------------------网络收集之-------------------------------------

关于结构体和C++类的内存地址问题

今天终于有时间写点东西了~ 太爽了  *_*  很多人都知道C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的。下面我们以类来说明问题,如果类的问题通了,结构体也也就没问题啦。 类分为成员变量和成员函数,我们先来讨论成员变量。 一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一个成员变量的地址。(在定义类对象的同时这些成员变量也就被定义了)我们来以一段代码说明问题: //类的定义class K{
public:
  K(){k = 12;}
  ~K(){}
  int k;
}; //类的使用//... K kTemp;
  printf("%d--%d\n",&kTemp,&kTemp.k);
  printf("%d--%d\n",sizeof(K),sizeof(kTemp.k));
  int *i = (int*)(&kTemp);
  int w = *i;
  printf("%d\n",w); 运行上面的代码,结果如下:1310588--1310588
4--4
12
很明显,类的内存大小和其唯一的成员变量的内存大小是一致的。内存地址也是一致的。他们甚至可以相互转换。换成结构体结果也是一样。网友可以自己运行上面代码来进行确认。 这个时候,可能有人会提出疑问了。那么成员函数又如何?上面得代码就好像类没有任何成员函数一样,根本说明不了问题。 呵呵,所有的函数都是存放在代码区的,不管是全局函数,还是成员函数。要是成员函数占用类的对象空间,那么将是多么可怕的事情:定义一次类对象就有成员函数占用一段空间。 我们再来补充一下静态成员函数的存放问题吧:静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员,就像我前面提到的,所有函数都存放在代码区,静态函数也不例外。所有有人一看到 static 这个单词就主观的认为是存放在全局数据区,那是不对的。(当然正在看我博客的网友应该不至于犯这样的问题,但是林子大了什么鸟都有嘛,我在这里多写两句,希望各位网友不要嫌我啰嗦哦) 原创,请转帖者附上下面网址

链接地址

 

 

----------第二篇------------

c++是一种面向对象的编程语言,它向下保持了对c的兼容,同时也允许程序员能够自由的操控内存,虽然会带来一些问题,但这不是我们要探讨的问题,略过不表。类是对某种对象的定义,包含变量和方法,也可以理解为现实生活中一类具有共同特征的事务的抽象,他是面向对象语言的基础。所以类是不占有内存的,可是如果类生成实例那么将会在内存中分配一块内存来存储这个类。

    类的实例在内存中是如何分配内存的,有什么需要我们注意的,下面将慢慢到来。

    比如下面一个类:

    class A

    {};

    从形式上看,它似乎什么有没有,事实上它不止隐含了一个构造函数和一个析构函数,还有一些操作符重载函数,比如“=”。如果类A被实例话,如A a;在内存会占据多大的空间呢?有人可能会说4,也有人会说0,还有人会说1,说1的就对了,为什么会是1呢?原因有很多,如果我们定义一个数组A b[10];如果上面是0,这样的局面将会很尴尬,所以A这样一个空类,编译器会给它一个字节来填充。  

    增加一个变量,(字节对齐默认都是4)

    class  A

   {

     public:

          int i;

   }

  

   类A的实例将占据4个字节的内存,sizeof(A) = 4

   变量i 的初值被编译器指定位0xcdcdcdcd。

    再增加一个变量,

   class A

   {

       public:

      int  i;

      int  l;

   }

   此时按照变量生命的先后顺序,i被放在低地址上,l紧随其后。

   实例占用8个字节,sizeof(A) = 4*2 = 8

   如果累里面含有函数:

  class A

  {

     public:

      int i;

      int l;

       int add(int x,int y){return (x+y);}

  };

  有些人可能会说类的大小是12,事实上sizeof(A) = 8;

  为什么会这样,这是因为sizeof访问的程序的数据段,而函数地址则被保存在代码段内,所以最后的结果是8.

  再看下面这个情况

  class A

  {

      public:

         int i;

         int l;

         static int s;

        int add(int x,int y){return (x+y)};

  };

此时sizeof(A)大小仍为8,这里留给读者去思考为什么?(^-^)。

当类里面含有虚函数时,情况会如何呢?

  class A

  {

      public:

         int i;

         int l;

         static int s;

         virtual void Say(){};

         int add(int x,int y){return (x+y)};

  };

  因为含有虚函数,所以类里面将含有一个虚指针vptr,指向该类的虚表vtbl,一个指针占用四字节的地址,所以sizeof(A) = 12

  虚指针放在类实例地址的最低位置,

  比如 A *a = new A;

  我们可以这样给变量i赋值

int *p = (int *)a;
  p++;
  *p = 1;//把i的值赋为1.

如果类作为派生类,内存将如何分配呢?

这种情况虽然有些复杂,但并不是说不好理解。

他有多少个父类每个父类的大小加起来在加上自身就是sizeof的大小。

转自:链接地址

 

//-----C++类对象内存结构[讲得很好] -------

首先介绍一下C++中有继承关系的类对象内存的布局: 
在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。 
对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。 之后是子类自己的成员变量数据。 
对于子类的子类,也是同样的原理。但是无论继承了多少个子类,对象中始终只有一个虚函数表指针。 
 
 
 
为了探讨C++类对象的内存布局,先来写几个类和函数 
首先写一个基类: 
class Base 

public: 
virtual void f() { cout<< "Base::f"<< endl; } 
virtual void g() { cout<< "Base::g"<< endl; } 
virtual void h() { cout<< "Base::h"<< endl; } 
int base; 
protected: 
private: 
}; 
然后,我们多种不同的继承情况来研究子类的内存对象结构。 
1. 无虚函数集继承 
 
//子类1,无虚函数重载 
class Child1 : public Base 

public: 
virtual void f1() { cout<< "Child1::f1"<< endl; } 
virtual void g1() { cout<< "Child1::g1"<< endl; } 
virtual void h1() { cout<< "Child1::h1"<< endl; } 
int child1; 
protected: 
private: 
}; 
这个子类Child1没有继承任何一个基类的虚函数,因此它的虚函数表如下图: 
 
 
我们可以看出,子类的虚函数表中,先存放基类的虚函数,在存放子类自己的虚函数。 
 
2. 有一个虚函数继承 
//子类2,有1个虚函数重载 
class Child2 : public Base 

public: 
virtual void f() { cout<< "Child2::f"<< endl; } 
virtual void g2() { cout<< "Child2::g2"<< endl; } 
virtual void h2() { cout<< "Child2::h2"<< endl; } 
int child2; 
protected: 
private: 
}; 
 
当子类重载了父类的虚函数,则编译器会将子类虚函数表中对应的父类的虚函数替换成子类的函数。 
3. 全部虚函数都继承 
//子类3,全部虚函数重载 
class Child3 : public Base 

public: 
virtual void f() { cout<< "Child3::f"<< endl; } 
virtual void g() { cout<< "Child3::g"<< endl; } 
virtual void h() { cout<< "Child3::h"<< endl; } 
protected: 
int x; 
private: 
}; 
 
 
 
4. 多重继承 
多重继承,即类有多个父类,这种情况下的子类的内存结构和单一继承有所不同。 
 
我们可以看到,当子类继承了多个父类,那么子类的内存结构是这样的: 
子类的内存中,顺序 
 
5. 菱形继承 
 
 
6. 单一虚拟继承 
 
 
虚拟继承的子类的内存结构,和普通继承完全不同。虚拟继承的子类,有单独的虚函数表, 另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是自己的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。 
如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。 
因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换。 
 
7. 菱形虚拟继承 
 
结论: 
(1) 对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。 
(2) 对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。 
(3) 虚函数表中,先存放父类的虚函数,再存放子类的虚函数 
(4) 如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。 
(5) 对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。其中每一个父类拷贝都包含一个虚函数表指针。如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。 
(6) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

来源:链接地址

//-------------------------------------更新中的自己的了解...----------------------------------------------

....20/6/2011....

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。

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

C++类的内存地址存放问题 的相关文章

  • 事件委托 EventHandler 。

    事件就是当对象或类状态发生改变时 xff0c 对象或类发出的信息或通知 发出信息的对象或类称为 34 事件源 34 对事件进行处理的方法称为 34 接收者 34 通常事件源在发出状态改变信息时 它并不知道由哪个事件接收者来处理 这就需要一种
  • XML文件转换成字符串互相转换操作

    System Xml XmlDocument doc 61 new System Xml XmlDocument 新建对象 doc Load 34 filePath 34 XML文件路径 string content 61 doc Inne
  • dataSerVer操作方法总结

    using System using System Collections Generic using System Linq using System Text using System Data using System IO usin
  • ubuntu无法ping www.baidu.com问题

    1 使用ifconfig 查看ip 发现地址正常 2 查看dns ip地址正常 还是无法通ping www baidu com 后来把静态地址配置该为动态地址配置后成功
  • ToolStrip和ToolStripButton的用法

    假设我的toolstrip里面有三个toolstripbutton分别是tsp1 tsp2 tsp3依次加载 xff0c 如何设置tsp3显示在toolstrip的第一个按钮 ToolStripItem tsm 61 toolStrip1
  • c#多维数组的建立及操作 总结

    1C 如何定义和使用多维数组 不建议使用ArrayList xff0c 当数组里的元素是值类型在操作的时候会出现大量的装箱与拆箱步骤性能会损失许多 xff0c 而是应该用什么类型的元素创建什么类型的数组 xff0c 除非你的元素有交叉或不确
  • 在TreeView查找某一节点

    在TreeView 查找某一节点 xff0c 通常有两种方法 xff0c 一种是递归的 xff0c 一种不是递归 xff0c 但都是深度优先算法 其中 xff0c 非递归方法效率高些 xff0c 而递归算法要简洁一些 第一种 xff0c 递
  • C#的数据类型总结

    C 的数据类型可以分为3 类 数值类型 引用类型 指针类型 指针类型仅在不安全代码中使用 一 值类型 值类型包括简单值类型和复合型类型 简单值类型可以再细分为整数类型 字符类型 实数类型和布尔类型 xff1b 而复合类型则是简单类型的复合
  • C#中OpenFileDialog获取文件名和文件路径的常用方法.

    System IO Path GetFullPath openFileDialog1 FileName 绝对路径 System IO Path GetExtension openFileDialog1 FileName 文件扩展名 Syst
  • 操作XML 报错:根级别上的数据无效 和 给定编码中的字符无效 解决办法

    根级别上的数据无效 解决如下 private void button1 Click object sender EventArgs e try XmlDocument doc 61 new XmlDocument string file 6
  • DataGridRow的创建

    用原始datagridview的列名赋值的时候找不到列名 xff0c 用索引就可以 xff0c 不知道是怎么回事 DataGridViewRow dr 61 new DataGridViewRow dr CreateCells this d
  • 常用方法和属性列表

    BitConvert islittle 判断大小端 Array reverse 反排列数组
  • System.Windows.Forms.Timer与System.Timers.Timer的区别

    NET Framework里面提供了三种Timer xff1a System Windows Forms Timer System Timers Timer System Threading Timer VS NET 2005默认只有一个T
  • c++中scanf和printf

    xfeff xfeff scanf函数一般格式是 xff1a scanf 格式控制 输出表列 printf函数的一般格式是 printf 格式控制 输出表列 例3 4 用scanf和printf函数进行输入和输出 include lt io
  • win10无法上网,连网显示黄色三角形探号

    1 打开网络属性 2 打开无线电源开关 3 重启电脑 4 使用网络疑难解答 5 重启DHCP
  • 计算机程序的思维逻辑 (12) - 函数调用的基本原理

    xfeff xfeff 栈 上节我们介绍了函数的基本概念 xff0c 在最后我们提到了一个系统异常java lang StackOverflowError xff0c 栈溢出错误 xff0c 要理解这个错误 xff0c 我们需要理解函数调用
  • c语言中函数调用的原理

    xfeff xfeff 一 函数参数传递机制的基本理论 函数参数传递机制问题在本质上是调用函数 xff08 过程 xff09 和被调用函数 xff08 过程 xff09 在调用发生时进行通信的方法问题 基本的参数传递机制有两种 xff1a
  • 栈中函数调用原理_详解

    xfeff xfeff 函数调用是程序设计中的重要环节 xff0c 本文就函数调用的过程进行分析 一 eip ebp esp介绍 EIP xff0c EBP xff0c ESP都是系统的寄存器 xff0c 里面存储的是些地址 xff0c 我
  • C++中如何定义动态数组

    xfeff xfeff 首先 xff1a 为什么需要动态定义数组呢 xff1f 这是因为 xff0c 很多情况下 xff0c 在预编译过程阶段 xff0c 数组的长度是不能预先知道的 xff0c 必须在程序运行时动态的给出 但是问题是 xf
  • 数组和指针、数组指针和指针数组

    xfeff xfeff 数组和指针 数组指针和指针数组 函数指针和指针函数 数组标识符的意义 静态和动态创建的数组的本质区别 标识符类型判断方法 html view plain copy print include lt iostream

随机推荐

  • 函数中的指针分配的内存怎么释放

    xfeff xfeff 被调用函数里动态分配的内存要不要手动释放 20 我想手动释放来着 xff0c 但是指针是在被调用函数里声明的 xff0c 在调用函数里释放不了 我的被调用函数返回的就是这个指针 xff0c 我也不能在被调用函数里释放
  • duilib各种布局的作用,相对布局与绝对布局的的意义与用法

    duilib各种布局的作用 xff0c 相对布局与绝对布局的的意义与用法 原文 http blog csdn net zhuhongshu article details 38531447 主题 Duilib 转载请说明原出处 xff0c
  • wpf中:xaml中的命名空间的引入方法

    xfeff xfeff wpf中 xff1a xaml中的命名空间的引入 本文章已收录于 xff1a 在xaml中如有要使用c 数据类型 xff0c 那么需要引入c 的命名空间 xff0c 如需要使用String类 xff0c 则需要引入S
  • WPF Application启动界面设置——

    xfeff xfeff 本文章已收录于 xff1a 设置WPF从不同界面启动可以通过设置 StartupUri 属性完成 http blog csdn net bamboo slit article details 7164848 设置St
  • 【2.路由器接口配置】

    1 ip route 0 0 0 0 43 0 0 0 0 43 接口的IP地址 2 ip address ip地址 43 子网掩码 pos framing sdh sonet pos flag s1s0 2 0 3 4 5
  • Linux(debian7)操作基础(七)之LightDM详解及使用

    LightDM自启动默认登录 使用命令sudo vim etc lightdm lightdm conf 将 autologin user 61 更改为 autologin user 61 登录用户名 xff0c 保存退出 更改主机名 xf
  • c#读取指定路径的配置文件

    xfeff xfeff ExeConfigurationFileMap map 61 new ExeConfigurationFileMap map ExeConfigFilename 61 64 34 F App1 config 34 C
  • C#创建com组件

    xfeff xfeff 本文详细阐述如何用C 创建COM组件 xff0c 并能用VC6 0等调用 并附有完整测试通过的代码 废话不多说 xff0c 下面开始介绍 xff1a 开发工具 xff1a VS2010 VS2010命令提示符在 xf
  • Windows下静态链接库的使用

    xfeff xfeff 静态链接是指将一个或多个静态链接库 xff08 lib文件 xff09 在Link时期和调用该库的程序一起形成exe文件 网上关于静态链接库的理论叙述多且详尽 xff0c 我就不再造轮子了 xff0c 此处仅说明一下
  • 异步消息的传递-回调机制

    xfeff xfeff 软件模块之间总是存在着一定的接口 xff0c 从调用方式上 xff0c 可以把他们分为三类 xff1a 同步调用 回调和异步调用 同步调用是一种阻塞式调用 xff0c 调用方要等待对方执行完毕才返回 xff0c 它是
  • C++回调机制的几种实现方式

    xfeff xfeff Callback Callback的本质是设置一个函数指针进去 xff0c 然后在需要触发某个事件时调用该方法 比如Windows的窗口消息处理函数就是这种类型 比如下面的示例代码 xff0c 我们在Download
  • C/C++函数调用的几种方式总结

    xfeff xfeff 本篇文章主要是对C C 43 43 函数调用的几种方式进行了详细的总结介绍 xff0c 需要的朋友可以过来参考下 xff0c 希望对大家有所帮助 调用函数时 xff0c 计算机常用栈来存储传递给函数的参数 栈是一种先
  • Aspose.Cells.dll操作exel

    xfeff xfeff 1 创建execl xff08 不需要服务器或者客户端安装office xff09 public void DCExexl DataTable dt Workbook wb 61 new Workbook Works
  • 使用Aspose.Cells组件生成Excel文件实例

    xfeff xfeff 这篇文章主要介绍了使用Aspose Cells组件生成Excel文件的方法 xff0c 大家参考使用吧 生成带表头的Excel文件 xff0c 格式如下显示 当然更复杂的一些也可以通过 合并单元格的方法 public
  • VS2010中添加lib库引用

    xfeff xfeff 一 VS2010中添加lib库引用 1 菜单 项目 gt 属性 gt 配置属性 gt 链接器 gt 输入 附加依赖项 加入库名 如 my API lib 或是在cpp源文件中用代码 pragma comment li
  • C++静态库与动态库

    xfeff xfeff 这次分享的 宗旨 是 让大家学会创建与使用静态库 动态库 xff0c 知道静态库与动态库的区别 xff0c 知道使用的时候如何选择 这里不深入介绍静态库 动态库的底层格式 xff0c 内存布局等 xff0c 有兴趣的
  • 在mysql设置root用户密码的方法

    在mysql设置root用户密码的方法
  • C++中的“error:LNK2005 已经在*.obj中定义”异常

    xfeff xfeff C 43 43 中的 error LNK2005 已经在 obj中定义 异常问题 异常现象如下 xff1a C 43 43 中的 error LNK2005 已经在 obj中定义 异常问题 图1 error LNK2
  • 指令和数据都用二进制代码存放在内存中,从时空观角度回答CPU如何区分读出的代码是指令还是数据

    xfeff xfeff 指令用来确定 做什么 和 怎样做 xff0c 数据是 做 的时候需要原始数 计算机可以从时间和空间两方面来区分指令和数据 xff0c 在时间上 xff0c 取指周期从内存中取出的是指令 xff0c 而执行周期从内存取
  • C++类的内存地址存放问题

    了解C 43 43 类地址的存放和分配等问题 xff0c 能帮助我们更深入 更清晰了解类的组成及其使用 自己目前不是很清楚 xff0c 先收集一些网上资料 xff0c 而后再慢慢补充增加的了解 网络收集之 关于结构体和C 43 43 类的内