C++C++

(Boolan) C++ 类型大小和内存分布(虚函数指针、虚表、

2017-05-05  本文已影响314人  故事狗
题目要求

回答:

(以下大部分都是基于x64编译器下的windows平台的gcc version 5.3.0 (GCC)编译器的测试结果,不能其他平台也能得出完全一致的结论,如果在x32下编译结果会指出)
由于class相较于struct,默认的成员就是private,代码中没有特地强调private

Fruit类型的大小所占的内存(x64编译器下的结构) 4 * 8 = 32 Byte

注:虚函数指针因为是一个指针,其大小应该为4个字节,但在此我想说,如果使用x64编译器生成的64位程序的指针大小为8个字节。(一个只含有虚函数的struct,x64编译旗下,虚函数指针为8字节;x86编译器上虚函数指针和普通指针没啥区别,都是4个字节)。
在后续我有详细的测试论证过程。

Fruit类型的大小所占的内存(x86编译器下的结构) 4 * 8 = 32 Byte Apple类型的大小所占的内存(x64编译器下的结构) 5 * 8 = 40 Byte Apple类型的大小所占的内存(x86编译器下的结构) 5 * 8 = 40 Byte
关于答案以下是非常详细的测试和推理,篇幅较长,感谢您阅读,希望您多多指正。

答案分析:

- Fruit类和Apple类的相关定义

        class Fruit {
            int no;
            double weight;
            char key;
        public:
            void print() {   }
            virtual void process() {   }
        };

        class Apple : public Fruit {
            int size;
            char type;
        public:
            void save() {   }
            virtual void process() {   }
        };

再看代码之前,先简单说明一下代码的功能

测试定义顺序对内存的影响
the memory of Fruit8---------------------
Address of Fruit8: 0x 0x7ffcdb6a4110 | Size = 24
88  28  40  00  00  00  00  00  
00  00  00  00  00  00  00  00  
01  00  00  00  00  00  00  00  
-------------------------------

1 结果输出测试类型的名称
2 结果输出该类型的对象的地址和该类型的大小
3 结果输出对应地址下的内容(按字节,以十六进制的方式输出)

Fruit9的内存 Fruit9的内存图

函数问题

那么我们先来验证一下,函数到底会不会影响类型的大小呢?
二话不说,先上代码~~~~~

1 首先可以看出,添加了构造函数,并没有影响类型的内存大小,都还是32字节,说明** 构造函数,并不影响类型的大小**
2 其次,观察内存空间不难发现,图中画双框的部分的内存很相似,而且大小也正是八个字节的大小,只要研究清楚这个是什么,也就明白了类型的大小到底是怎么一回事。

现在知道了一直困扰我们的八个字节是来自与虚函数的定义,那么,问题接着就有来了,虚函数的所占内存的大小是多少? 是否遵循对其的原则呢?二话不说,赶快上代码测试!!

虚函数的内存问题

原始 Fruit5(抽象类) Fruit6(x64环境下结果) Fruit6(x86环境下结果) Fruit7(x64环境下结果) Fruit7(x86环境下结果) 多个虚函数

1 由原始数据和Fruit5(纯虚函数的抽象类)的输出的结果可以看到,虽然Fruit5不能创建对象,但是不难看出两者的大小是相同的,所以虚函数和纯虚函数占用的空间相同
2 由Fruit6的输出的结果可以得出,在x86和x64平台的结果不相同,*** 在32位平台的虚函大小为4字节,在64位平台下的虚函数的大小为8字节***
3 由Fruit7可以看出,虚函数所占内存大小的分配规则,符合对齐的规则,x64平台下,虚函数加char,会浪费7个字节的空间,x86平台下会浪费3个字节。
4 由Fruit10可以看出,多个虚函数的情况,实际占用与一个虚函数的情况相同。

现在已经完成了类型大小的整理,但是还差一件事,就是父类和子类的关系。

父类和子类

  1. 子类会继承父类的对齐系数,子类的成员是依据父类的对齐系数来计算的
  2. 子类的虚函数,不对大小产生影响
    class Fruit2 {
        int no;
        double weight;
        char key;
    public:
        Fruit2(int n, double w, char k) :no(n), weight(w), key(k) {}
        void print() {   }
        virtual void process() {   }
    };

    class Apple2 : public Fruit2 {
        int size;
        char type;
    public:
        Apple2(int s, char t, int n, double w, char k) :size(s), type(t), Fruit2(n, w, k) {}
        void save() {   }
        virtual void process() {   }
    };

    //定义顺序调整(虚函数同名)(优化后)
    class Fruit4 {
        char key;
        int no;
        double weight;
    public:
        Fruit4(int n, double w, char k) :no(n), weight(w), key(k) {}
        void print() {   }
        virtual void process() {   }
    };

    class Apple4 : public Fruit4 {
        char type;
        int size;
    public:
        Apple4(int s, char t, int n, double w, char k) :Fruit4(n, w, k),  size(s), type(t){}
        void save() {   }
        virtual void process() {   }
    };
Fruit2的系列 Fruit4的系列 Fruit2系列的分析 整理后的Fruit4的系列的分析

由之前的分析可以看出来,对于子类来说,虚函数指针是相同的位置,子类成员所占的内从空间始终在父类之后,父类空间后面所剩下的位置,可以与子类共用

子类的虚函数所站类型的大小,和数量,是否同名无关,始终处于最上方,且大小固定(为一个对齐系数的大小)

虚表问题

上一篇 下一篇

猜你喜欢

热点阅读