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

来源:互联网 发布:先锋电视网络看电视 编辑:程序博客网 时间:2024/06/07 19:37

之前用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 0
原创粉丝点击