C++类的大小及虚函数表

之前用C的时候,停挺经常计算结构体空间的,现在突然用C++了,好像还不知道C++类大小怎么算的。

1. 类的组成

我也不知道这么说是否正确,我觉得类主要有两类成员:数据成员,成员函数,并且
* 数据成员有静态和非静态之分
* 函数成员有静态,非静态和虚函数(virtual)之分

数据成员
在C++类中,数据成员的初始化是按照类中声明的先后顺序初始化,而与函数参数的传入顺序没有关系
其中,静态函数为所有类成员所共有,不计入某个类的大小,静态成员的初始化一般在类的外部实现
如:

xxx.h

class base{
  private:
      static int i;
}

xxx.cpp

int base::i=1;

注意:不要在头文件定义(初始化静态数据成员),大多数情况下会引起重复定义错误。
成员的大小计算应该和结构体一样,下面我们来验证一下

class basic{
    static int a;
    char b[10];
    int c;
};

int basic::a=1;


int main ()
{
    basic test;
    cout<<"size:"<<sizeof(test)<<endl;
    return 0;
}

输出为16,可以看出,a不占此类内存,b占10个字节,c四个字节,64位系统8字节对其,故为16

* 函数成员 *
类与结构体相比,多了属于自己的函数,类的非静态和静态函数都不占用类的内存,但是虚函数占用一个地址字节的内存。
而且无论定义多少个虚函数,都只占用一个地址字节的内存。

class basic{
    private:
        static int a;
        char b[10];
        int c;

    public:
        static void fun1(){cout<<"fun1"<<endl;}
        void fun2(){cout<<"fun2"<<endl;}
        virtual void fun3(){cout<<"fun3"<<endl;}
        virtual void fun4(){cout<<"fun4"<<endl;}


};
int basic::a=1;

int main ()
{
    basic test;
    cout<<"size:"<<sizeof(test)<<endl;
    return 0;
}

输出为24,结论正确

既然扯到虚函数了,就说说虚函数吧
如果一个类中存在虚函数,编译器会做以下三件事
* 为该类分配一个虚函数表,它存有虚函数在执行器的地址
* 在该类中安插一个虚指针,指向该类的虚表
* 将每一个虚函数的入口地址存放在虚函数表中相应的slot

所以在类的内存中储存的,就是指向虚函数表的虚指针
那接下来,我们来看看,类里面的数据成员和虚函数是怎样储存的,在这里,我先输出了类成员地址,为了方便,我直接将成员替换为了公有成员

#include <iostream>
using namespace std;
class basic{
    public:
        static int a;
        char b[10];
        int c;

        static void fun1(){cout<<"fun1"<<endl;}
        void fun2(){cout<<"fun2"<<endl;}
        virtual void fun3(){cout<<"fun3"<<endl;}
        virtual void fun4(){cout<<"fun4"<<endl;}
};

int basic::a=1;

int main ()
{
    basic test;
    cout<<"size:"<<sizeof(test)<<endl;

    cout<<"addr of test:"<<&test<<endl;
    cout<<"addr of test.a:"<<&test.a<<endl;
    for(int i=0;i<10;++i)
    {
        cout<<"addr of test.b["<<i<<"]:"<<(void *)&(test.b[i])<<endl;
        //这里得强制转换一下,不然坑爹的cout会把他当做字符串处理的
    }
    cout<<"addr of of test.c:"<<&test.c<<endl;

    return 0;
}

输出:

size:24
addr of test:0x7ffca3bd1980
addr of test.a:0x602080
addr of test.b[0]:0x7ffca3bd1988
addr of test.b[1]:0x7ffca3bd1989
addr of test.b[2]:0x7ffca3bd198a
addr of test.b[3]:0x7ffca3bd198b
addr of test.b[4]:0x7ffca3bd198c
addr of test.b[5]:0x7ffca3bd198d
addr of test.b[6]:0x7ffca3bd198e
addr of test.b[7]:0x7ffca3bd198f
addr of test.b[8]:0x7ffca3bd1990
addr of test.b[9]:0x7ffca3bd1991
addr of of test.c:0x7ffca3bd1994

可以看出来,a的地址明显不在类的地址范围内,类的地址与第一个元素地址相差8个字节,即64位机的地址字节,这8个字节中储存虚函数指针,下面验证这一点

#include <iostream>
using namespace std;


class basic{
    public:
        static int a;
        char b[10];
        int c;

        static void fun1(){cout<<"fun1"<<endl;}
        void fun2(){cout<<"fun2"<<endl;}
        virtual void fun3(){cout<<"fun3"<<endl;}
        virtual void fun4(){cout<<"fun4"<<endl;}
};

int basic::a=1;


int main ()
{
    basic test;
    cout<<"size:"<<sizeof(test)<<endl;

    cout<<"addr of test:"<<&test<<endl;
    cout<<"addr of test.a:"<<&test.a<<endl;
    for(int i=0;i<10;++i)
    {
        cout<<"addr of test.b["<<i<<"]:"<<(void *)&(test.b[i])<<endl;
        //这里得强制转换一下,不然坑爹的cout会把他当做字符串处理的
    }
    cout<<"addr of of test.c:"<<&test.c<<endl;

    void (*pfun3)(void); //函数指针
    void (*pfun4)(void); //函数指针

    pfun3 = (void(*)(void))*(long *)(*(long*)(&test));
    // 这里应该用typedef void(*FUN)(void)处理下会比较好,我只是想这样写写 long 刚好是64bit
    pfun3();
    pfun4 = (void(*)(void))*((long *)(*(long*)(&test))+1);
    // (long *)(&test) 将test的地址转化为系统字节长度地址再取出来  在这个地址上存放这虚函数表的地址
    // *(long *)(&test) 将虚函数指针所指向的对象取出来  得出的值应该是虚函数表的地址 同时也是第一个虚函数的地址
    // *(long *)*(long *)(&test) 从虚函数表的地址中取出内容  即第一个虚函数
    // (long *)*(long *)(&test)+1  虚函数表地址+1 即第二个虚函数地址  取出来即是fun4
    pfun4();
    return 0;
}

输出

size:24
addr of test:0x7ffe4ed2da70
addr of test.a:0x602080
addr of test.b[0]:0x7ffe4ed2da78
addr of test.b[1]:0x7ffe4ed2da79
addr of test.b[2]:0x7ffe4ed2da7a
addr of test.b[3]:0x7ffe4ed2da7b
addr of test.b[4]:0x7ffe4ed2da7c
addr of test.b[5]:0x7ffe4ed2da7d
addr of test.b[6]:0x7ffe4ed2da7e
addr of test.b[7]:0x7ffe4ed2da7f
addr of test.b[8]:0x7ffe4ed2da80
addr of test.b[9]:0x7ffe4ed2da81
addr of of test.c:0x7ffe4ed2da84
fun3
fun4

从上面实验可以看出,类的内存结构如下图所示

1

fun3和fun4多写了一个括号。。。

刚入门c++找对象,如果有出错的地方还请大神指教

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值