菜鸟笔记
提升您的技术认知

sizeof类大小

 决定C ++中对象的大小的因素:

1.所有非静态数据成员的大小
2.数据成员的顺序
3.字节对齐或字节填充
4.其直接基类的大小虚函数的存在
5.  正在使用的编译器
6.继承模式(虚拟继承)

 

一、使用sizeof计算类对象所占空间大小

  需要注意,对类做sizeof运算时,并不是简单地把各个成员所占的内存数量相加。需要注意成员顺序不同,可能需要字节补齐。

编程实例:

#include <iostream>
using namespace std;

class A  
{
public:
	int i; //int占用4个字节
};

class B
{
public:
	char ch; //char占用1个字节
};

class C
{
public:
	int i;
	short j;//short占用2个字节
};

class D  //D共占用8个字节
{
public:
	int i;  //int占用4个字节
	short j;  //short和char共占用3个字节,由于这里最宽的            
	char ch;  //基本类型是int的4个字节,再填充一个字节凑成4的倍数
};

class E  //E共占用8个字节
{
public:     
	int i;
	int ii;  //两个int共占用8个字节
	short j;  //1个short + 2个char = 4个字节,刚好是最宽
	char ch; //基本类型int的大小的整数倍,不需要再填充字节
	char chr;
};

int main()
{
	cout << "sizeof(A) = " << sizeof(A) << endl;  //4
	cout << "sizeof(B) = " << sizeof(B) << endl;  //1
	cout << "sizeof(C) = " << sizeof(C) << endl;  //8
	cout << "sizeof(D) = " << sizeof(D) << endl;  //8
	cout << "sizeof(E) = " << sizeof(E) << endl;  //12
	getchar();
	return 0;
}

小结:

1.类中的数据成员顺序不同,类所占的内存大小可能不同;
2.注意需要 字节对齐或字节填充 的情况;
3.派生类的内存大小需要加上基类的内存大小。

 拓展知识:关于字节对其你和字节填充

编程实例:

class C { 
        char c; 
        int int1; 
        int int2; 
        int i; 
        long l; 
        short s; 
};

 分析:

  这个类的大小是24字节。尽管char c只消耗1个字节,但将为它分配4个字节,剩下的3个字节将被浪费(留空)。这是因为下一个成员是int,它占用4个字节。如果我们不进入下一个(4)字节来存储这个整数成员,那么这个整数的内存访问/修改周期将是2个读周期。所以编译器会为我们做这个补位。

图解:

二、使用sizeof计算含有虚函数的类对象的空间大小

  虚函数的存在将在类中添加4个字节的虚拟表指针,这将被添加到类的大小。 同样,在这种情况下,如果类的基类已经直接或通过其基类具有虚函数,那么这个额外的虚函数将不会添加任何类的大小。 虚拟表指针在类层次结构中是通用的。 

编程实例:

#include <iostream>
using namespace std;

class Base  //Base占用的内存大小为4,即1个int
{
public:
	Base(int x) :a(x)
	{

	}
	void print()  //函数不占用内存
	{
		cout << "base" << endl;
	}
private:
	int a;  
};

class Derived :public Base //Derived的内存大小=Base的大小+Derived中的一个int,
{                           //即4 + 4 = 8
public:
	Derived(int x) :Base(x - 1), b(x)
	{

	}
	void print()
	{
		cout << "derived" << endl;
	}
private:
	int b;
};

class A  //A共占用8个字节
{
public:
	A(int x) :a(x)
	{

	}
	virtual void print() //虚函数产生一个隐含的虚表指针成员,占4个字节
	{
		cout << "A" << endl;
	}
private:
	int a; //占4个字节
};

class B :public A //b所占内存 = A所占内存 + 4
{
public:
	B(int x) :A(x - 1), b(x)
	{

	}
	virtual void print()
	{
		cout << "B" << endl;
	}
private:
	int b;  //占4个字节
};

int main()
{
	Base obj1(1);
	cout << "size of Base obj is " << sizeof(obj1) << endl;  //4
	Derived obj2(2);
	cout << "size of Derived obj is " << sizeof(obj2) << endl;  //8

	A a(1);
	cout << "size of A obj is " << sizeof(a) << endl;  //8
	B b(2);
	cout << "size of B obj is " << sizeof(b) << endl;  //12

	getchar();
	return 0;
}

 小结:

1.  普通函数不占用内存;
2.只要有虚函数就会占用一个指针大小的内存,原因是系统多用一个这镇维护这个类的虚函数表。

三、使用sizeof计算虚拟继承的类对象的空间大小

要点:在C++中,有时由于某些原因,我们不得不使用虚拟继承。当我们使用虚拟继承时,在该类中,虚拟基类指针将会有4个字节的开销。

编程实例:

#include <iostream>
using namespace std;

class A //空类的大小不为零,一般来说它是1个字节,
{       //确保两个不同的对象具有不同的地址是非零的

};

class B  //1个字节
{

};

class C :public A, public B { //1个字节

};

class D :virtual public A {  //虚函数的存在将在类中添加4个字节
	                         //的virtual table pointer

};

class E :virtual public A, virtual public B { //虚继承A有4个字节+
	                                          //虚继承B有4个字节

};

class F  
{
public:
	int a;  //占4个字节
	static int b;  //静态成员的空间不在类的实例中,
	         //而是像全局变量一样在静态存储区
};

int F::b = 10;  //在类外初始化静态成员


int main()
{
	cout << "sizeof(A) = " << sizeof(A) << endl;  //1
	cout << "sizeof(B) = " << sizeof(B) << endl;  //1
	cout << "sizeof(C) = " << sizeof(C) << endl;  //1
	cout << "sizeof(D) = " << sizeof(D) << endl;  //4
	cout << "sizeof(E) = " << sizeof(E) << endl;  //8
	cout << "sizeof(F) = " << sizeof(F) << endl;  //4

	getchar();
	return 0;
}

知识扩展:为什么C ++中空类的大小不为零?

例如:

#include<iostream> 
using namespace std; 
  
class Empty {}; 
  
int main() 
{ 
  cout << sizeof(Empty); 
  return 0; 
} 

输出结果:

1

分析:

  空类的大小不为零。一般是1个字节。确保两个不同的对象具有不同的地址是非零的。

例如:

#include<iostream> 
using namespace std; 
  
class Empty { }; 
  
int main() 
{ 
    Empty a, b; 
  
    if (&a == &b) 
      cout << "a 和 b的地址相同 " << endl; 
    else
      cout << "a 和 b的地址不同 " << endl; 
  
   return 0; 
} 

输出结果:

a 和 b的地址不同

还有另外一种情况,你可能会感到奇怪:


#include<iostream> 
using namespace std; 
  
class Empty { }; 
  
class Derived: Empty { int a; }; 
  
int main() 
{ 
    cout << sizeof(Derived); 
    return 0; 
} 

 输出:

4

为什么是4,而不是5?

  原因是C++有一条规则表明空基类不需要用单独的字节表示。因此编译器可以在空基类的情况下自由优化。

<本文完> 

参考资料:

1)https://www.geeksforgeeks.org

2)https://www.cprogramming.com

3)《C和C++程序员面试秘笈》