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 注:虚表还是只有一张,不会因为增加了新的虚函数而多出另一张来,新的虚函数的指针将添加在复制了的虚表的后面。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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搭建本