(1)计算一个结构体的sizeof时,要注意内存对齐
原则:
1:数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储。
2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)
3:收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍.不足的要补齐.
举例说明:typedef struct bb
{
int id; //[0]....[3]
double weight; //[8].....[15] 原则1
float height; //[16]..[19],总长要为8的整数倍,补齐[20]...[23] 原则3
}BB;
typedef struct aa
{
char name[2]; //[0],[1]
int id; //[4]...[7] 原则1
double score; //[8]....[15]
short grade; //[16],[17]
BB b; //[24]......[47] 原则2
}AA;
int main()
{
AA a;
cout<<sizeof(a)<<" "<<sizeof(BB)<<endl;
return 0;
}
结果是
48 24
(2)空类的sizeof为1,单一继承的空类空间也为1,多重继承的空类空间还为1.
class Base
{
public:
Base();
~Base();
};
sizeof(Base)=1;
(3)静态变量存储在全局数据区,而sizeof计算栈中分配的大小,是不会计算在内的
(4)非空类大小
class Base
{
public:
Base();
virtual ~Base(); //每个实例都有虚函数表
void set_num(int num) //普通成员函数,为各实例公有,不归入sizeof统计
{
a=num;
}
private:
int a; //占4字节
char *p; //4字节指针
};
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st; //非实例独占
int d; //占4字节
char *p; //4字节指针
};
int main()
{
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
return 0;
}
结果是
12
20
分析:Base类里的int a;char *p;占8个字节。而虚析构函数virtual ~Base();的指针占4子字节。其他成员函数不归入sizeof统计。
Derive类首先要具有Base类的部分,也就是占12字节。int d;char *p;占8字节static int st;不归入sizeof统计所以一共是20字节。
在考虑在Derive里加一个成员char c;
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
private:
static int st;
int d;
char *p;
char c;
};
这个时候,结果为:
12
24
一个char c;增加了4字节,说明类的大小也遵守类似class字节对齐的补齐规则。归纳:
1.类的大小为类的非静态成员数据的类型大小之和,也就是说静态成员数据不作考虑。
2.普通成员函数与sizeof无关。
3.虚函数由于要维护在虚函数表,所以要占据一个指针大小,也就是4字节。
4.类的总大小也遵守类似class字节对齐的,调整规则。
(5)非空类大小(虚函数继承)子类只是共用父类的虚函数表,因此一旦父类里有虚函数,子类的虚函数将不计入sizeof大小。
class Base
{
public:
Base(){};
virtual ~Base(){};
void set_num(int num)
{
a=num;
}
virtual int get_num()
{
return a;
}
private:
int a;
char *p;
};
class Derive:public Base
{
public:
Derive():Base(){};
~Derive(){};
virtual int get_num() //虚函数继承
{
return d;
}
private:
static int st;
int d;
char *p;
char c;
};
int main()
{
cout<<sizeof(Base)<<endl;
cout<<sizeof(Derive)<<endl;
return 0;
}
在Base类里添加了virtual int get_num()函数,而子类也重新实现了virtual int get_num()函数。
但是结果依然是
12
24
(6)非空类大小(虚继承)class Top
{
protected:
int x;
public:
Top(int n):x(n){cout<<"Top"<<endl;}
virtual ~Top(){}
};
class Left:virtual public Top
{
protected:
int y;
public:
Left(int m,int n):Top(m){y=n;cout<<"Left"<<endl;}
};
class Right:public virtual Top
{
protected:
int z;
public:
Right(int m,int n):Top(m){z=n;cout<<"Right"<<endl;}
};
class Bottom:public Left,public Right
{
int w;
public:
Bottom(int i,int j,int k,int m):Top(i),Left(i,j),Right(i,k),w(m)
{
cout<<"Bottom"<<endl;
}
};
int main()
{
Bottom b(1,2,3,4);
cout<<"sizeof(b) "<<sizeof(b)<<","<<sizeof(Bottom)<<endl;
cout<<sizeof(Left)<<","<<sizeof(Right)<<","<<sizeof(Top)<<endl;
for(int i=0;i<sizeof(b);i+=4)
cout<<*(reinterpret_cast<int*>((&b)+i))<<"________"<<endl;
system("PAUSE");
}
结果如下:
Top
Left
Right
Bottom
sizeof(b) 28,28
16,16,8
4657244________
2367460________
0________
746________
44________
5701724________
4________
请按任意键继续. . .
也就是说Top为占8字节:
int x; //4字节
virtual ~Top(){} //基类的虚表入口,4字节
接着看Left跟Right都是16字节。本来除了Top的8字节,Left里只有int y; 占4字节,还有4字节占在那里?
由于是虚继承,虚继承的子类都要包含一个指向基类的指针,从而实现动态联编。
一次,要额外加4字节的空间。所以一共是8+4+4=16字节。
Right同理。
再看Bottom的大小为28字节,这个是怎么算的呢?
虚继承是棱形继承,基类大小为8字节.
而Bottom为普通多继承,因此,Bottom的大小应该是Bottom部分+Left部分+Right部分+各自指向基类的指针+基类大小(虚继承导致只有一个基类实例)。
Top
/ /
/ /
Left Right
/ /
/ /
Bottom
现在算算,基类8字节+Left4字节+Right4字节+4字节指向基类的指针*2+Bottom4字节=28字节。