sizeof计算类的大小

1 sizeof一个空类

1
2
3
class A { };
 
cout<<sizeof(A)<<endl;//1

注:class A是一个空类型,它的实例不包含任何信息,本来求sizeof应该是0。

但当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。

至于占用多少内存,由编译器决定。Visual Studio 2008中每个空类型的实例占用一个byte的空间。

 

2 sizeof一个带有构造和析构函数的类

1
2
3
4
5
6
7
8
9
10
11
12
13
class B
 
{
 
public:
 
  B() {}
 
   ~B() {}
 
};
 
cout<<szieof(B)<<endl;//1

  

注:class B在class A的基础上添加了构造函数和析构函数。

       由于构造函数和析构函数的调用与类型的实例无关(调用它们只需要知道函数地址即可),在它的实例中不需要增加任何信息。

       所以sizeof(B)和sizeof(A)一样,在Visual Studio 2008中都是1。

 

3 sizeof一个带有虚函数的类

1
2
3
4
5
6
7
8
9
10
11
12
13
class C
 
{
 
public:
 
  C() {}
 
  virtual ~C() {}
 
};
 
cout<<sizeof(C)<<endl;//4

  

注:class C在class B的基础上把析构函数标注为虚拟函数。C++的编译器一旦发现一个类型中有虚拟函数,

       就会为该类型生成虚函数表,并在该类型的每一个实例中添加一个指向虚函数表的指针。

       在32位的机器上,一个指针占4个字节的空间,因此sizeof(C)是4。

       C++标准规定类的大小不为0,空类的大小为1,当类不包含虚函数和非静态数据成员时,其对象大小也为1。

       如果在类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针指向虚函数表VTable,

       在32位机器上,一个对象会增加4个字节来存储此指针,它是实现面向对象中多态的关键。而虚函数本身和其他成员函数一样,是不占用对象的空间的。

 

4 sizeof一个带有成员变量的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class D {
 
    char ch;
 
    void func() { }
 
};
 
class E {
 
    char ch1; //占用1字节
 
    char ch2; //占用1字节
 
    virtual void func() { }
 
};
class F {
 
    int in;
 
    virtual void func() { }
 
};
 
cout << "D的大小"<< sizeof(D) << endl;//1
cout << "E的大小" << sizeof(E) << endl;//8
cout << "F的大小" << sizeof(E) << endl;//8

 注:类和结构体一样,需要考虑数据对齐和补齐规则

5 sizeof简单继承类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
class COneMember
 
{
 
public:
 
    COneMember(int iValue = 0){m_iOne = iValue;};
 
private:
 
    int m_iOne;
 
};
 
内存结构:00 00 00 00 //m_iOne
 
class CTwoMember:public COneMember
 
{
 
private:
 
    int m_iTwo;
 
};
 
内存结构:
 
00 00 00 00 //m_iOne
 
CC CC CC CC //m_iTwo
 
注:子类成员接在父类成员之后
 
class CThreemember:public CTwoMember
 
{
 
public:
 
    CThreemember(int iValue=10) {m_iThree = iValue;};
 
private:
 
    int m_iThree;
 
};
内存结构:
00 00 00 00 //m_iOne
 
CC CC CC CC //m_iTwo
 
0A 00 00 00 //m_iThree
 
注:孙类成员接在子类之后,再再继承就依此类推了。
 
cout<<sizeof(COneMember)<<endl;//4
cout<<sizeof(CTwoMember)<<endl;//8
cout<<sizeof(CThreeeMember)<<endl;//12   

 

6 sizeof多重继承类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class ClassA
 
{
 
public:
 
    ClassA(int iValue=1){m_iA = iValue;};
 
private:
 
    int m_iA;
 
};
 
  
 
class ClassB
 
{
 
public:
 
    ClassB(int iValue=2){m_iB = iValue;};
 
private:
 
    int m_iB;
 
};
 
  
 
class ClassC
 
{
 
public:
 
    ClassC(int iValue=3){m_iC = iValue;};
 
private:
 
    int m_iC;
 
};
 
  
 
class CComplex :public ClassA, public ClassB, public ClassC
 
{
 
public:
 
    CComplex(int iValue=4){m_iComplex = iValue;};
 
private:
 
    int m_iComplex;
 
};
 
内存结构:
01 00 00 00  //A
 
02 00 00 00  //B
 
03 00 00 00  //C
 
04 00 00 00  //Complex
 
注:也是父类成员先出现在前边,我想这都足够好理解。
 
cout<<sizeof(CComplex )<<endl;//16

  

7 sizeof 虚继承的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CTwoMember:virtual public COneMember
 
{
 
private:
 
    int m_iTwo;
 
};
 
内存结构:
 
E8 2F 42 00 //指针,指向一个关于偏移量的数组,且称之虚基类偏移量表指针
 
CC CC CC CC // m_iTwo
 
00 00 00 00 // m_iOne(虚基类数据成员)
 
长度:12<br><br>注:virtual让长度增加了4,其实是多了一个指针。

  

8 sizeof一个闭合虚继承类

内存结构:

14 30 42 00 //ClassB的虚基类偏移量表指针

02 00 00 00 //m_iB

C4 2F 42 00 //ClassC的虚基类偏移量表指针

03 00 00 00 //m_iC

04 00 00 00 //m_iComplex

01 00 00 00 //m_iA

长度:24

注:虚基类的成员m_iA只出现了一次,而且是在最后边。虚继承利用一个“虚基类偏移量表指针”来使得虚基类即使被重复继承也只会出现一次。

 

9 sizeof一个带有static数据成员的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CStaticNull
 
{
 
public:
 
    CStaticNull(){printf("Construct/n");}
 
    ~CStaticNull(){printf("Desctruct/n");}
 
    static void Foo(){printf("Foo/n");}
 
    static int m_iValue;
 
};
 
内存结构:和空类相同
长度:1
注:static成员不会占用类的大小,static成员的存在区域为静态区,可认为它们是“全局”的,只是不提供全局的访问而已,这跟C的static其实没什么区别。

  

10 带有一个虚函数的空类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class CVirtualNull
 
{
 
public:
 
    CVirtualNull(){printf("Construct/n");}
 
    ~CVirtualNull(){printf("Desctruct/n");}
 
    virtual void Foo(){printf("Foo/n");}
 
};
 
内存结构:
00 31 42 00 //指向虚函数表的指针(虚函数表后面简称“虚表”)
 
00423100:(虚表)
41 10 40 00 //指向虚函数Foo的指针
 
00401041:
E9 78 02 00 00 E9 C3 03 … //函数Foo的内容(看不懂)
 
长度:4
注:虚函数的类长度就增加了4,这个4其实就是个指针,指向虚函数表的指针,上面这个例子中虚表只有一个函数指针,值就是“0x00401041”,指向的这个地址就是函数的入口了

  

11 sizeof一个继承带虚函数的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class CVirtualDerived : public CVirtualNull
 
{
 
public:
 
    CVirtualDerived(){m_iVD=0xFF;};
 
    ~CVirtualDerived(){};
 
private:
 
    int m_iVD;
 
};
 
长度:8
内存结构:
3C 50 42 00 //虚表指针
FF 00 00 00 //m_iVD
 
0042503C:(虚表)
23 10 40 00 //指向虚函数Foo的指针,如果这时候创建一个CVirtualNull对象,会发现它的        虚表的内容跟这个一样
 
注:由于父类带了虚函数,子类就算没有显式声明虚函数,虚表还是存在的,虚表存放的位置跟父类不同,但内容是同的,也就是对父类虚表的复制。

  

12 sizeof子类带虚函数,基类带虚函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class CVirtualDerived: public CVirtualNull
 
{
 
public:
 
    CVirtualDerived(){m_iVD=0xFF;};
 
    ~CVirtualDerived(){};
 
    virtual void Foo2(){printf("Foo2/n");};
 
private:
 
    int m_iVD;
 
};
 
内存结构:
24 61 42 00 //虚表指针
FF 00 00 00 //m_iVD
 
00426124:(虚表)
23 10 40 00
50 10 40 00
 
长度:8
注:虚表还是只有一张,不会因为增加了新的虚函数而多出另一张来,新的虚函数的指针将添加在复制了的虚表的后面。

  

posted @   Truman001  阅读(1505)  评论(1编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
点击右上角即可分享
微信分享提示