关于C++中sizeof
1. 空类
class A
{
};
cout<<sizeof(A)
//结果为1
空类也是可以被实例化的,实例化就是在内存中分配地址,按理说空类的大小应该为0,因为没有存储任何数据,但是因为空类可以实例化,要是大小为0的话,就无法分配内存了,所以空类隐含的加上了一个字节,所以在用sizeof的时候,输出为1
2. 单一数据
class A
{
public:
int a;
};
cout<<sizeof(A);
//结果为4
一般int 类型占用4个字节
3. 多个数据
class A
{
public:
char a;
int b;
double c;
};
class B
{
public:
char a;
double b;
int c;
}
cout<<sizeof(A);
//结果为16
cout<<sizeof(B);
//结果为24
对于A的计算,char 1字节,int的首地址偏移为4的倍数,所以要补3个字节,所以现在1+3+4=8字节,再计算double,现在首地址为8,所以不用补字节,所以总大小为1+3+4+8=16字节,且16字节为8的倍数。
对于B的计算 char 1字节,double的首地址要求为8的倍数,所以现在大小为1+7+8=16字节,16字节为4的倍数,按理说不用补齐,但是16+4=20,不是最宽字节的倍数,所以应该补4字节,所以总大小为 1+7+8+4+4=24字节。以下是字节补齐的准则,结构体和类类似。
比较节省空间的原则是,按照类型大小从小到大排列
默认对齐方式
字节对齐的细节和具体编译器实现相关,但一般而言,满足以下三个准则(也是VC默认对齐方式):
1,结构体变量的首地址能够被其最宽基本类型成员的大小所整除(0能被任意整数整除);
2,结构体每个成员相对于结构体首地址的偏移量都是该成员类型大小的整数倍,如有需要编译器会在成员之间加上填充字节;
3,结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
以上引自https://blog.csdn.net/u010927488/article/details/49202071
4. 成员函数
class A
{
public:
void f(){cout<<"F";};
void g(){cout<<"G";};
}
cout<<sizeof(A);
//结果是1
在定义类的时候,成员函数被放在了内存的代码区,我们只需要知道函数的地址,就可以执行这个函数,所以我们不是每实例化一个对象,就存储成员函数的代码,这样太浪费空间了。因为所有实例化的对象的成员函数所执行的逻辑都是一样,所以大家同时去找一个函数进行调用就可以了。
所以在实例化的时候,只会分配数据的空间,而不会分配函数的空间。下面有个例子来说明这个问题。
class A
{
public:
void f(){cout<<"F";};
}
A* a=NULL;
a->f();
//结果打印出 F
虽然没有实例化a,但是依然可以使用a来调用成员函数打印出了正确结果,这就说明成员函数的存储和对象的实例化没有关系,但是注意一点,如果成员函数要操作类的数据,那么如果没有实例化对象的话,也就是说这个数据没有分配空间,所以成员函数无法对数据操作,编译可以通过,但是运行时会出错,所以最好不要用没有实例化的对象调用成员函数。
C++程序的内存格局通常分为四个区:全局数据区(data area),代码区(code area),栈区(stack area),堆区(heap area)(即自由存储区)。全局数据区存放全局变量,静态数据和常量;所有类成员函数和非成员函数代码存放在代码区;为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区;余下的空间都被称为堆区。
根据这个解释,我们可以得知在类的定义时,类成员函数是被放在代码区,而类的静态成员变量在类定义时就已经在全局数据区分配了内存,因而它是属于类的。对于非静态成员变量,我们是在类的实例化过程中(构造对象)才在栈区或者堆区为其分配内存,是为每个对象生成一个拷贝,所以它是属于对象的。应当说明,常说的“某某对象的成员函数”,是从逻辑的角度而言的,而成员函数的存储方式,是从物理的角度而言的,二者是不矛盾的。
下面我们再来讨论下类的静态成员函数和非静态成员函数的区别:静态成员函数和非静态成员函数都是在类的定义时放在内存的代码区的,因而可以说它们都是属于类的,但是类为什么只能直接调用静态类成员函数,而非静态类成员函数(即使函数没有参数)只有类对象才能调用呢?原因是类的非静态类成员函数其实都内含了一个指向类对象的指针型参数(即this指针),因而只有类对象才能调用(此时this指针有实值)
引自https://blog.csdn.net/fuzhongmin05/article/details/59112081。
5. 虚函数
class A
{
public:
virtual void f(){};
virtual void g(){};
};
cout<<sizeof(A);
//结果是4或8;
刚刚说了成员函数不分配空间,其实这里不矛盾的,如果有虚函数的话,系统会分配一个指向虚函数表的指针,32位地址的机器上,指针是4个字节,而64位地址的机器是8个字节,不过这也要看编译器,为了兼容32为机器,编译器会按照32位地址的方式来编译,我用的dev C++ 建立一个win32控制台程序,打印出的结果就是4字节。
类在实例化的时候,最前面的几个字节存的就是指向虚函数表的指针。
以下还有一个多继承的例子
class A
{
public:
virtual void a(){
};
virtual void b(){
};
};
class B
{
public:
virtual void c(){
};
virtual void d(){
};
};
class C:public A,B
{
public:
virtual void e(){
};
virtual void f(){
};
};
int main()
{
cout<<sizeof(C);
}
//结果是8或16
分配了两个指针,一个指向A的虚表,一个指向B的虚表
这里有一篇文章详细解析了虚函数表,我觉得非常好!至少我看的比较明白
https://blog.csdn.net/haoel/article/details/1948051/