类中内容在内存中到底是如何分配的呢?

2023-05-16


分类:

      一个类,有成员变量:静态与非静态之分;而成员函数有三种:静态的、非静态的、虚的。
      那么这些个东西在内存中到底是如何分配的呢?
      以一个例子来说明:

      [html]view plaincopyprint?

  1.       #include"iostream.h"  
  2.       class CObject  
  3.       {  
  4.       public:  
  5.       static int a;  
  6.       CObject();  
  7.       ~CObject();  
  8.       void Fun();  
  9.       
  10.       private:  
  11.       int m_count;  
  12.       int m_index;  
  13.       };  
  14.       void CObject::Fun()  
  15.       {  
  16.       cout<<"Fun\n"<<endl;  
  17.       }  
  18.       CObject::CObject()  
  19.       {  
  20.       cout<<"Construct!\n";  
  21.       }  
  22.       CObject::~CObject()  
  23.       {  
  24.       cout<<"Destruct!\n";  
  25.       }  
  26.       int CObject::a=1;  
  27.       void main()  
  28.       {  
  29.       cout<<"Sizeof(CObject):"<<sizeof(CObject)<<endl;  
  30.       //CObject::Fun();  
  31.       cout<<"CObject::a="<<CObject::a<<endl;  
  32.       CObject myObject;  
  33.       cout<<"sizeof(myObject):"<<sizeof(myObject)<<endl;  
  34.       cout<<"sizeof(int)"<<sizeof(int)<<endl;  
  35.       
  36.       }  
这是我的一段测试代码, 
      运行结果是: 
      Sizeof(CObject):8 
      CObject::a=1 
      Construct! 
      sizeof(myObject):8 
      sizeof(int)4 
      Destruct! 
      我有疑问如下: 
      (1) C++中,应该是对象才会被分配内存空间吧??为什么CObject内存大小是8,刚好和两个 成员变量的大小之和一致!难道还没实例化的时候,类就 已经有了内存空间了? 
      (2)当对象生成了之后,算出的内存大小怎么还是8,函数难道不占用内存空间吗?至少应该放个 函数指针在里面的吧?内存是怎样布局的?
      (3) 静态成员应该是属于类的,怎么类的大小中没有包含静态成员的大小?
      下面分别解答如下:
      1)Sizeof(CObject)是在编译时就计算了的,一个类定义了,它所占的内存编译器就已经知道了,这时只是得到它占用的大小,并没有分配内存操作 。也可以这样想:编译器肯定知道大小了,这与分配内存空间无关,知道大小了,以后实例化了才能知道要分配多大。
      2)类的普通成员、静态成员函数是不占类内存的,至于你说的函数指针在你的类中有 虚函数的时候存在一个 虚函数表指针,也就是说如果你的类里有虚函数则 sizeof(CObject)的值会增加4个字节。
      其实 类的成员函数 实际上与 普通的 全局函数一样。 
      只不过编译器在编译的时候,会在成员函数上加一个参数,传入这个对象的指针。
      成员函数地址是全局已知的,对象的内存空间里根本无须保存成员函数地址。 
      对成员函数(非虚函数)的调用在编译时就确定了。 
      像 myObject.Fun() 这样的调用会被编译成形如 _CObject_Fun( &myObject ) 的样子。
      函数是不算到sizeof中的,因为函数是代码,被各个对象共用,跟数据处理方式不同。对象中不必有函数指针,因为对象没必要知道它的各个函数的地址(调 用函数的是其他代码而不是该对象)。 
      类的属性是指类的数据成员,他们是实例化一个对象时就为数据成员分配内存了,而且每个对象的数据成员是对立的,而成员函数是共有的~ 
      静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员。总之,程序中的所有函数都是位于代码区的。
      3)静态成员并不属于某个对象,sizeof取的是对象大小。
      知道了上面的时候,就可以改一下来看看:
      我也补充一些: 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      double m_count;  //这里改成了double 
      int  m_index; 
      }; 
      这个类用sizeof()测出来的大小是 2*sizeof(double)=16 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      char m_count;  //这里改成了char 
      int  m_index; 
      }; 
      大小是2*sizeof(int)=8 
      class CObject 
      { 
      public: 
      static int a; 
      CObject(); 
      ~CObject(); 
      void Fun(); 
      private: 
      double m_count;  //这里改成了double 
      int  m_index; 
      char  c; 
      }; 
      sizeof(char)+sizeof(int) <sizeof(double) 所以大小是2*sizeof(double) 
      其实这里还有一个是内存对齐的问题。
      空类大小是1。 
      另外要注意的一些问题:

      先看一个空的类占多少空间?

      class Base
      {
      public:
      Base();
      ~Base();
      }; 

class Base { public: Base(); ~Base(); };

      注意到我这里显示声明了构造跟析构,但是sizeof(Base)的结果是1.

      因为一个空类也要实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含 的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的sizeof为1。

      而析构函数,跟构造函数这些成员函数,是跟sizeof无关的,也不难理解因为我们的sizeof是针对实例,而普通成员函数,是针对类体的,一个类的成 员函数,多个实例也共用相同的函数指针,所以自然不能归为实例的大小,这在我的另一篇博文有提到。

      接着看下面一段代码

      [html]view plaincopyprint?

  1.       class Base   
  2.       {   
  3.       public:   
  4.       Base();                   
  5.       virtual ~Base();         //每个实例都有虚函数表   
  6.       void set_num(int num)    // 普通成员函数,为各实例公有,不归入sizeof统计   
  7.       {   
  8.       a=num;   
  9.       }   
  10.       private:   
  11.       int  a;                  //占4字节   
  12.       char *p;                 //4字节指针   
  13.       };   
  14.       
  15.       class Derive:public Base   
  16.       {   
  17.       public:   
  18.       Derive():Base(){};         
  19.       ~Derive(){};   
  20.       private:   
  21.       static int st;         //非实例独占   
  22.       int  d;                     //占4字节   
  23.       char *p;                    //4字节指针   
  24.       
  25.       };   
  26.       
  27.       int main()     
  28.       {     
  29.       cout<<sizeof(Base)<<endl;   
  30.       cout<<sizeof(Derive)<<endl;   
  31.       return 0;   
  32.       }   
class Base { public: Base(); virtual ~Base(); //每个实例都有虚函数表 void set_num(int num) //普通成员函数,为各实例公有,不归入sizeof统计 { a=num; } private: int a; //占4字节 char *p; //4字节指针 }; class Derive:public Base { public: Derive():Base(){}; ~Derive(){}; private: static int st; //非实例独占 int d; //占4字节 char *p; //4字节指针 }; int main() { cout<<sizeof(Base)<<endl; cout<<sizeof(Derive)<< endl; return 0; }

      结果自然是

      12

      20

      Base类里的int  a;char *p;占8个字节。

      而虚析构函数virtual ~Base();的指针占4子字节。

      其他成员函数不归入sizeof统计。

      Derive类首先要具有Base类的部分,也就是占12字节。

      int  d;char *p;占8字节

      static int st;不归入sizeof统计

      所以一共是20字节。

      在考虑在Derive里加一个成员char c;

  1.       class Derive:public Base 
  2.       { 
  3.       public: 
  4.       Derive():Base(){}; 
  5.       ~Derive(){}; 
  6.       private: 
  7.       static int st; 
  8.       int  d; 
  9.       char *p; 
  10.       char c; 
  11.       
  12.       }; 
class Derive:public Base { public: Derive():Base(){}; ~Derive(){}; private: static int st; int d; char *p; char c; };

      这个时候,结果就变成了

      12

      24

      一个char c;增加了4字节,说明类的大小也遵守类似class字节对齐,的补齐规则。

      具体的可以看我那篇《5分钟搞定字节对齐》

      至此,我们可以归纳以下几个原则:

      1.类的大小为类的非静态成员数据的类型大小之和,也 就是说静态成员数据不作考虑。

      2.普通成员函数与sizeof无关。

      3.虚函数由于要维护在虚函数表,所以要占据一个指针大小,也就是4字节。

      4.类的总大小也遵守类似class字节对齐的,调整规则。

      转载自:http://www.blue1000.com/bkhtml/c151/2010-11/69613.htm

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

类中内容在内存中到底是如何分配的呢? 的相关文章

  • 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 类的内
  • 介绍了如何取成员函数的地址以及调用该地址

    xfeff xfeff 摘要 xff1a 介绍了如何取成员函数的地址以及调用该地址 关键字 xff1a C 43 43 成员函数 this指针 调用约定 一 成员函数指针的用法 在C 43 43 中 xff0c 成员函数的指针是个比较特殊的
  • 类中内容在内存中到底是如何分配的呢?

    xfeff xfeff 分类 xff1a 一个类 xff0c 有成员变量 xff1a 静态与非静态之分 xff1b 而成员函数有三种 xff1a 静态的 非静态的 虚的 那么这些个东西在内存中到底是如何分配的呢 xff1f 以一个例子来说明