(转)C++类所占内存大小计算

摘要:
Sizeof=1是空类。为什么编译器说它是1?sizeof=1类的非虚拟成员函数不计算在内,无论它是否是静态的。事实上,成员函数的大小不包括在类对象中,类占用的内存不包括成员函数的尺寸,虚拟成员函数除外。sizeof=4的原理类似于C,无论类中有多少个虚拟函数,虚拟表的起始地址都可以保存在类中,虚拟函数地址可以通过偏移量等算法获得。对于sizeof=832位的操作系统,int占4字节,char占1字节,内存对齐的3字节占8字节。

C++类所占内存大小计算

转载时请注明出处和作者联系方式
文章出处:
http://blog.csdn.net/chenchong08
作者联系方式:vision_chen@yeah.net

说明:笔者的操作系统是32位的。

class A {};
sizeof( A ) = ?
sizeof( A ) = 1
明明是空类,为什么编译器说它是1呢?
空类同样可以实例化,每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址.所以sizeof( A )的大小为1.

 
class B
{
public:
  B() {}
  ~B() {}
  void MemberFuncTest( int para ) { }
  static void StaticMemFuncTest( int para ){  }
};
sizeof( B ) = ?
sizeof( B ) = 1
类的非虚成员函数是不计算在内的,不管它是否静态。

ps:成员函数还是以一般的函数一样的存在。a.fun()是通过fun(a.this)来调用的所谓成员函数只是在名义上是类里的。其实成员函数的大小不在类的对象里面,类所占内存的大小不包括成员函数的大小,虚拟成员函数除外。同一个类的多个对象共享函数代码。而我们访问类的成员函数是通过类里面的一个指针实现,而这个指针指向的是一个table,table里面记录的各个成员函数的地址(当然不同的编译可能略有不同的实现)。所以我们访问成员函数是间接获得地址的。所以这样也就增加了一定的时间开销,这也就是为什么我们提倡把一些简短的,调用频率高的函数声明为inline形式(内联函数)。


class C
{
 C(){}
 virtual ~C() {}
};
sizeof( B ) = ?
sizeof( B ) = 4
类D有一个虚函数,存在虚函数的类都有一个一维的虚函数表叫虚表,虚表里存放的就是虚函数的地址了,因此,虚表是属于类的。这样的类对象的前四个字节是一个指向虚表的指针,类内部必须得保存这个虚表的起始指针。在32位的系统分配给虚表指针的大小为4个字节,所以最后得到类C的大小为4.


class D
{
 D(){}
 virtual ~D() {}
 virtual int VirtualMemFuncTest1()=0;
 virtual int VirtualMemFuncTest2()=0;
 virtual int VirtualMemFuncTest3()=0;
};
sizeof( D ) = ?
sizeof( D ) = 4
原理同类C,不管类里面有多少个虚函数,类内部只要保存虚表的起始地址即可,虚函数地址都可以通过偏移等算法获得。


class E
{
 int  m_Int;
 char m_Char;
};
sizeof( E ) = ?
sizeof( E ) = 8
32位的操作系统int占4个字节,char占一个字节,加上内存对齐的3字节,为8字节。


class F : public E
{
 static int s_data ;
};
int F::s_data=100;
sizeof( F ) = ?
sizeof( F ) = 8
类F为什么跟类E一样大呢?类F的静态数据成员被编译器放在程序的一个global data members中,它是类的一个数据成员,但是它不影响类的大小,不管这个类实际产生了多少实例还是派生了多少新的类,静态成员数据在类中永远只有一个实体存在,而类的非静态数据成员只有被实例化的时候,他们才存在.但是类的静态数据成员一旦被声明,无论类是否被实例化,它都已存在.可以这么说,类的静态数据成员是一种特殊的全局变量.


class G : public E
{
 virtual int VirtualMemFuncTest1(int para)=0;
 int m_Int;
};
class H : public G
{
 int m_Int;
};
sizeof( G ) = ?
sizeof( H ) = ?
sizeof( G ) = 16
sizeof( H ) = 20
可以看出子类的大小是本身成员的大小再加上父类成员的大小.如果父类还有父类,也加上父类的父类,这样一直递归下去。


class I : public D
{
 virtual int VirtualMemFuncTest1()=0;
 virtual int VirtualMemFuncTest2()=0;
};
sizeof( I ) = ?
sizeof( I ) = 4
父类子类工享一个虚函数指针,虚函数指针保留一个即可。

总结:
空的类也是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。
(一)类内部的成员变量:
普通的变量:是要占用内存的,但是要注意内存对齐(这点和struct类型很相似)。
static修饰的静态变量:不占用内存,原因是编译器将其放在全局变量区。
从父类继承的变量:计算进子类中
(二)类内部的成员函数:
非虚函数(构造函数、静态函数、成员函数等):不占用内存。
虚函数:要占用4个字节(32位的操作系统),用来指定虚拟函数表的入口地址。跟虚函数的个数没有关系。父类子类工享一个虚函数指针。

        构成对象本身的只有数据,任何成员函数都不隶属于任何一个对象,非静态成员函数与对象的关系就是绑定,绑定的中介就是this指针。成员函数为该类所有对象共享,不仅是处于简化语言实现、节省存储的目的,而且是为了使同类对象有一致的行为。同类对象的行为虽然一致,但是操作不同的数据成员。

测试代码如下:

  1. <SPAN style="FONT-SIZE: 18px">/* 
  2.  * file name   : main.cpp 
  3.  * description : test the size of c++'s class 
  4.  * create on   : 2012-05-31 
  5.  * create by   : chenchong 
  6.  * email         : vision_chen@yeah.net 
  7.  */  
  8.   
  9. #include<iostream>   
  10.   
  11. using namespace std;  
  12.   
  13. class A {};   
  14.   
  15. class B   
  16. {  
  17. public:  
  18.   B() {}  
  19.   ~B() {}  
  20.   void MemberFuncTest( int para ) { }  
  21.   static void StaticMemFuncTest( int para ){  }  
  22. };  
  23.   
  24. class C   
  25. {  
  26.  C(){}  
  27.  virtual ~C() {}  
  28. };  
  29.   
  30. class D   
  31. {  
  32.  D(){}  
  33.  virtual ~D() {}  
  34.  virtual int VirtualMemFuncTest1()=0;  
  35.  virtual int VirtualMemFuncTest2()=0;  
  36.  virtual int VirtualMemFuncTest3()=0;  
  37. };  
  38.   
  39. class E  
  40. {  
  41.  int  m_Int;  
  42.  char m_Char;  
  43. };  
  44.   
  45. class F : public E  
  46. {  
  47.  static int s_data ;  
  48. };  
  49. int F::s_data=100;  
  50.   
  51. class G : public E  
  52. {  
  53.  virtual int VirtualMemFuncTest1(int para)=0;  
  54.  int m_Int;  
  55. };  
  56. class H : public G  
  57. {  
  58.  int m_Int;  
  59. };  
  60.   
  61. class I : public D  
  62. {  
  63.  virtual int VirtualMemFuncTest1()=0;  
  64.  virtual int VirtualMemFuncTest2()=0;  
  65. };  
  66.   
  67. int main( int argc, char **argv )  
  68. {  
  69.  cout<<"sizeof( A ) = "<<sizeof( A )<<endl;  
  70.  cout<<"sizeof( B ) = "<<sizeof( B )<<endl;  
  71.  cout<<"sizeof( C ) = "<<sizeof( C )<<endl;  
  72.  cout<<"sizeof( D ) = "<<sizeof( D )<<endl;  
  73.  cout<<"sizeof( E ) = "<<sizeof( E )<<endl;  
  74.  cout<<"sizeof( F ) = "<<sizeof( F )<<endl;  
  75.  cout<<"sizeof( G ) = "<<sizeof( G )<<endl;  
  76.  cout<<"sizeof( H ) = "<<sizeof( H )<<endl;  
  77.  cout<<"sizeof( I ) = "<<sizeof( I )<<endl;  
  78.   
  79. #if defined( _WIN32 )   
  80.  system("pause");  
  81. #endif   
  82.  return 0;  
  83. }  
  84.   
  85. </SPAN>  
/*
 * file name   : main.cpp
 * description : test the size of c++'s class
 * create on   : 2012-05-31
 * create by   : chenchong
 * email         : vision_chen@yeah.net
 */

#include<iostream>

using namespace std;

class A {}; 

class B 
{
public:
  B() {}
  ~B() {}
  void MemberFuncTest( int para ) { }
  static void StaticMemFuncTest( int para ){  }
};

class C 
{
 C(){}
 virtual ~C() {}
};

class D 
{
 D(){}
 virtual ~D() {}
 virtual int VirtualMemFuncTest1()=0;
 virtual int VirtualMemFuncTest2()=0;
 virtual int VirtualMemFuncTest3()=0;
};

class E
{
 int  m_Int;
 char m_Char;
};

class F : public E
{
 static int s_data ;
};
int F::s_data=100;

class G : public E
{
 virtual int VirtualMemFuncTest1(int para)=0;
 int m_Int;
};
class H : public G
{
 int m_Int;
};

class I : public D
{
 virtual int VirtualMemFuncTest1()=0;
 virtual int VirtualMemFuncTest2()=0;
};

int main( int argc, char **argv )
{
 cout<<"sizeof( A ) = "<<sizeof( A )<<endl;
 cout<<"sizeof( B ) = "<<sizeof( B )<<endl;
 cout<<"sizeof( C ) = "<<sizeof( C )<<endl;
 cout<<"sizeof( D ) = "<<sizeof( D )<<endl;
 cout<<"sizeof( E ) = "<<sizeof( E )<<endl;
 cout<<"sizeof( F ) = "<<sizeof( F )<<endl;
 cout<<"sizeof( G ) = "<<sizeof( G )<<endl;
 cout<<"sizeof( H ) = "<<sizeof( H )<<endl;
 cout<<"sizeof( I ) = "<<sizeof( I )<<endl;

#if defined( _WIN32 )
 system("pause");
#endif
 return 0;
}


 

Windows 7 32位 VC 2010运行结果:

(转)C++类所占内存大小计算第1张

Linux(cent os 6.2 32位)运行结果:

 (转)C++类所占内存大小计算第2张

免责声明:文章转载自《(转)C++类所占内存大小计算》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇vs2013新建web项目异常 0x80070002 处理uniapp 富文本图片100%显示下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

LinuxC下获取UDP包中的路由目的IP地址和头标识目的地址

在接受到UDP包后,有时候我们需要根据所接收到得UDP包,获取它的路由目的IP地址和头标识目的地址。 (一)主要的步骤: 在setsockopt中设置IP_PKTINFO,然后通过recvmsg来获取struct in_pktinfo(struct in_pktinfo是struct msghdr中msg_control的成员).in_pktinfo 结构...

function类型(c++11)

1.c++五大可调用的对象   可调用的对象常常作为泛型算法的实参 1)函数 2)函数指针 函数名其实也是函数指针,只不过函数名是一个常量指针,它的值不能改变,只能指向该函数,不能改变它的值让它指向别的函数 void fun1(int a) { cout << a << endl; } void fun2(int a)...

[C++ STL] vector使用详解

一、概述 vector(向量): 是一种序列式容器,事实上和数组差不多,但它比数组更优越。一般来说数组不能动态拓展,因此在程序运行的时候不是浪费内存,就是造成越界。而vector正好弥补了这个缺陷,它的特征是相当于可分配拓展的数组(动态数组),它的随机访问快,在中间插入和删除慢,但在末端插入和删除快。 二、定义及初始化 使用之前必须加相应容器的头文件: #...

.Net Core如何优雅的实现中间件

在.Net Core的源码中,很多地方都有中间件的地方,Kestrel Server和Asp.net Core 等都用了中间件的设计,比如在Kestrel Server中,Http协议的1.0, 1.1, 2.0分别注册了不同的中间件从而导致不同方式的解析报文,这些要求了我们如何设计一个优雅的中间件框架,在MSDN 上这样描述了asp.net core的 ...

sscanf函数用法详解

名称: sscanf() - 从一个字符串中读进与指定格式相符的数据. 函数原型: Int sscanf( string str, string fmt, mixed var1, mixed var2 … ); int scanf( const char *format [,argument]… ); 说明: sscanf与scanf类似,都是用于输入的,...

再探NSString

再探NSString NSString应该是oc开发中最常用的一个数据类型了,这次对该类型再进行一次全方位的探索与总结。 NSString本质上属于OC类对象,继承于NSObject,遵守NSCopying, NSMutableCopying, NSSecureCoding协议。 NSMutableString与之类似,唯一不同的是它继承于NSStrin...