【C++】使用sizeof计算类对象所占空间大小-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++程序员面试秘笈》