C++之:类对象内存对齐

一、关于空间的思考

一个Class对象需要占用多大的内存空间。最权威的结论是:

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

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

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

二、例程

//注:以下注释是在mac os g++ 环境编译:g++ memAlign.cpp -o memAlign

//size = 1
//1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
class Car1{
};
void fun1(void)
{
    int size =0;
    Car1 objCar;
    size = sizeof(objCar);
    printf("%s is %d\n","Class Car1 Size",size);
}

//size = 8
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);
}

//size = 8
//2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;
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);
}

//size = 12
//3、因此一个对象的大小≥所有非静态成员大小的总和;
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);
}

//size = 1
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);
}

//size = 8
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);
}

//size = 8
//4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
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);
}

//size = 8
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);
}

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

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

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

int main()
{
    fun1();
    fun2();
    fun3();
    fun4();
    fun5();
    fun6();
    fun7();
    fun8();
}

在VC6.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字节对齐,这样解释就比较清楚了。)

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

#include <cstdio>
#include <iostream>
#define pos(type,member) (&((type *)0)->member)

class car{
public:
    car(){}
    ~car(){}
public:
    virtual void fun(){}
private:
    int c;
public:
    void print()
    {
        printf("%x\n",pos(car,c));
    }
};

int main()
{
    struct Node{
        int a ;
        char b;
        int c;
    };
    car objCar;
//    printf("%x\n",&((struct Node *)0)->b);
    printf("%x\n",pos(struct Node,b));
    printf("%x\n",pos(struct Node,c));
//    printf("%x\n",pos(class car,c));
    objCar.print();
    return 0;
}

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

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

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

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


参考文献

[1] http://www.cnblogs.com/xuanyuanchen/archive/2012/10/24/2737626.html

[2] http://blog.csdn.net/hackbuteer1/article/details/7883531

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值