c++中类对象的内存对齐

很多C++书籍中都介绍过,一个Class对象需要占用多大的内存空间。最权威的结论是:

*非静态成员变量总合。(not static)

*加上编译器为了CPU计算,作出的数据对齐处理。(c语言中面试中经常会碰到内存对齐的问题)

*加上为了支持虚函数(virtual function),产生的额外负担。

下面给出几个程序来看一下:

#include <iostream>

#include <cstdio>

#include <string>

using namespace std;

class Car1{

};



void fun1(void)

{

    int size =0;

    Car1 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car1 Size",size);

}



class Car2{

private:

    int nLength;

    int nWidth;

};

void fun2(void)

{

    int size = 0;

    Car2 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car2 Size",size);

}



class Car3{

private:

    int nLength;

    int nWidth;

    static int sHight;

};

void fun3(void)

{

    int size =0;

    Car3 objCar;

    size =sizeof(objCar);

    printf("%s is %d\n","Class Car3 Size",size);

}



class Car4{

private:

    char chLogo;

    int nLength;

    int nWidth;

    static int sHigh;

};

void fun4(void)

{

    int size =0;

    Car4 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car4 Size",size);

}



class Car5{

public:

    Car5(){};

    ~Car5(){};

public:

    void Fun(){};

};



void fun5(void)

{

    int size =0 ;

    Car5 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car5 Size",size);

}



class Car6{

public:

    Car6(){};

    ~Car6(){};

public:

    void Fun(){};

private:

    int nLength;

    int nWidth;

};

void fun6(void)

{

    int size = 0;

    Car6 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car6 Size",size);

}



class Car7{

public:

    Car7(){};

    virtual ~Car7(){};

public:

    void Fun(){};

};

void fun7(void)

{

    int size = 0;

    Car7 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car7 Size",size);

}



class Car8{

public:

    Car8(){};

    virtual ~Car8(){};

public:

    void Fun(){};

    virtual void Fun1(){}

};

void fun8(void)

{

    int size = 0;

    Car8 objCar;

    size = sizeof(objCar);

    printf("%s is %d\n","Class Car8 Size",size);

}



int main()

{

    fun1();

    fun2();

    fun3();

    fun4();

    fun5();

    fun6();

    fun7();

    fun8();

}

编译:g++ memAlign.cpp -o memAlign

输出结果:

Class Car1 Size is 1

Class Car2 Size is 8

Class Car3 Size is 8

Class Car4 Size is 12

Class Car5 Size is 1

Class Car6 Size is 8

Class Car7 Size is 8
Class Car8 Size is 8

ps:上述编译环境是在mac os下的g++

下面我们具体来分析一下这几个情况:

1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);

2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;

3、因此一个对象的大小≥所有非静态成员大小的总和;

4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;

5、虚继承的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr(virtual function table)指针指向虚函数表vfTable和一个vbPtr(virtual base pointer)指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);

6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;(请参考《c和指针》)

7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。

 参考:C++虚函数工作原理和(虚)继承类的内存占用大小计算

 在VC 6.0中,结果是

Class Car1 Size is 1

Class Car2 Size is 8

Class Car3 Size is 8

Class Car4 Size is 12

Class Car5 Size is 1

Class Car6 Size is 8

Class Car7 Size is 4

Class Car8 Size is 4

主要的不同点是:在Car7和Car8,在VC 6.0中虚函数指针占用4个字节,在gcc编译器中占用8个字节。

        也可以换一种说法是virtual函数指针在VC下以4字节对齐,在gcc下是8字节对齐,这样解释就比较清楚了。

二、编程实现成员在类或结构体中的偏移量

代码如下所示:

 1 #include <cstdio>

 2 #include <iostream>

 3 #define pos(type,member) (&((type *)0)->member)

 4 

 5 class car{

 6 public:

 7     car(){}

 8     ~car(){}

 9 public:

10     virtual void fun(){}

11 private:

12     int c;

13 public:

14     void print()

15     {

16         printf("%x\n",pos(car,c));

17     }

18 };

19 

20 int main()

21 {

22     struct Node{

23         int a ;

24         char b;

25         int c;

26     };

27     car objCar;

28 //    printf("%x\n",&((struct Node *)0)->b);

29     printf("%x\n",pos(struct Node,b));

30     printf("%x\n",pos(struct Node,c));

31 //    printf("%x\n",pos(class car,c));

32     objCar.print();

33     return 0;

34 }

其中关键的是找到函数能够实现计算成员在类中的偏移量,这里用了宏来实现的。

#define pos(type,member) (&((type *)0)->member)

(从地址0开始的一个type结构体或者类,其成员的地址就是成员所在类或结构体的偏移量)

上述程序的输出结果就是:4 8 8

你可能感兴趣的:(C++)