C++类的大小引发的思考

以下讨论仅针对无继承的情形。
一、无成员变量的空类
1.不含virtual关键字时为1

class A{};
int main(int argc, char* argv[])
{
    cout << sizeof(A) << endl; //结果输出为1
    return 0;
}

2.含virtual关键字时为一个指针的大小

class A{
    A(){}
    virtual ~A(){}
};
int main(int argc, char* argv[])
{
    cout << sizeof(A) << endl; //gcc -m32输出4,-m64输出8
    return 0;
}

二、含有成员变量
1.不含virtual关键字时为非静态成员变量大小

class A{
    A(){}
    ~A(){}
private:
    char str[3];//3字节
    int a[10];//40字节
    static int si;
}__attribute__((packed)); //取消字节对齐
int main(int argc, char* argv[])
{
    cout << sizeof(A) << endl; //取消字节对齐时输出43,默认为44
    return 0;
}

2.含有virtual关键字时为非静态成员变量大小+一个指针大小

class A{
    A(){}
    virtual ~A(){}
private:
    char str[3];//3字节
    int a[10];//40字节
    static int si;
}__attribute__((packed)); //取消字节对齐
int main(int argc, char* argv[])
{
    cout << sizeof(A) << endl;//gcc -m32取消字节对齐时输出47(默认48),-m64输出51(默认56)
    return 0;
}

三、思维扩展
当类中含有虚函数时,其实例对象中会多一个指针,该指针指向虚函数表,如何确定该指针在内存中的位置呢?从2.2的64位程序字节对齐差异的结果中,可以推断出该指针位于对象头部,也就是所有成员变量之前。以下代码可以对这一推断加以验证。

class A{
public:
    A(){}
    virtual void func(){cout << "call virtual func success!" << endl;}
    virtual void func2(){cout << "call virtual func2 success!" << endl;}
    void call_virtual(int *faddr){
        ((void(*)(void))*faddr)();//强制转换为函数指针并调用该函数
        faddr++;
        ((void(*)(void))*faddr)();//调用内存相邻的函数
    }
    virtual ~A(){cout << "destory object!" << endl;}
//private:
    char str[3];
    int a[10];
};//__attribute__((packed));

int main(int argc, char* argv[])
{
    A *pa = new A;
    A aa;
    int *paddr = (int*)pa;
    cout << pa << endl;
    cout << &(pa->str) << endl;
    cout << &(pa->a) << endl;

    cout << "virtual function table = " << hex << *paddr << endl;
    //((void(*)(void))*paddr)();
    //这里直接调用虚函数表里的函数会崩溃,gdb调试会看到在此处崩溃原因是
    //Program received signal SIGSEGV, Segmentation fault.
    //0x56557e84 in vtable for A ()
    //可以理解为该函数只有A类的对象才能调用,所以下面通过call_virtual这个非虚函数去间接调用虚函数
    pa->call_virtual((int*)*paddr);
    delete pa;
    
    paddr = (int*)&aa;
    cout << &aa << endl;
    cout << &(aa.str) << endl;
    cout << &(aa.a) << endl;
    cout << "virtual function table = " << hex << *paddr << endl;
    return 0;
}

程序执行结果和解读如下:

./virFunTable 
0x57047b70 //对象首地址
0x57047b74 //对象第一个成员变量的首地址,可以看到与对象首地址之前隔了一个指针的大小
0x57047b78 //因为内存对齐,所以第二个成员变量的起始地址并没有连续地跟在前一个变量之后
virtual function table = 565fce84 //对对象首地址解引用即可看到虚函数表起始地址
call virtual func success! //使用非虚函数成功的间接调用了虚函数表中的两个虚函数
call virtual func2 success!
destory object!
0xff8be8dc
0xff8be8e0
0xff8be8e4
virtual function table = 565fce84//这里可以确定虚函数表属于类,而非对象,即同一类的所有对象共享一个虚函数表
destory object!

通过以上执行结果可以确定,虚函数表指针位于对象头部空间,且同一类的所有对象的虚函数指针指向同一个虚函数表。

posted on 2022-03-04 21:28  OrangeGLC  阅读(72)  评论(0)    收藏  举报

导航