类的内存模型(类的内存结构(一))
类的内存结构包含什么?
静态成员变量和静态成员函数是不会计算在类的内存结构中的 ,因为静态static决定了它们早在编译期就确定了静态变量区中的地址 ,因此通常来说类的内存结构只包含普通成员变量 。 还有什么特殊情况下不止包含普通成员变量呢? 继承:当子类继承父类时,父类的普通成员变量同样会存储在子类的内存结构之中 虚函数:子类继承父类时 ,如果父类存在虚函数(父类存在虚函数 ,作为继承类的子类必定也存在虚函数) ,那么此时父类中存在一个虚函数指针 ,指向存储虚函数的地址 ,虚函数指针位于类内存结构的顶部 。 当virtual遇上继承:虚继承的存在 ,是用于面对菱形继承的问题 ,在这里假设将TrueBase称为一级父类 ,Base1 、Base2称为二级父类 ,Son称为三级子类 。如果Base1 、Base2同时作为TrueBase类的子类,而son类继承了Base1和Base2类 ,比如动物 ,羊和驼,羊驼(切勿当真) 。此时为了避免Son类中重复存在TrueBase类的成员变量 ,将Base1和Base2定义为TrueBase的虚拟继承子类(就像继承了但没有完全继承) 。 虚继承是什么原理呢?
虚继承实际上是通过虚基类指针来寻找TrueBase类的地址 ,和虚函数指针一样,是通过指针来指向内存地址 ,而不是像普通继承那样直接存入地址 ,形成空间浪费 。 加入了继承 、虚函数 、虚继承之后 ,内存结构究竟变得怎么样了呢?
(Base1: virtual public TrueBase; Base2: virtual Public TrueBase; Son: public Base1, public Base2)
vfptr即虚函数指针 ,vbptr是虚基类指针 ,vfptr在内存中处于vbptr之前 ,且子类通过vbptr寻找的基类存储在最后 。 肯定有人问为什么当发生虚继承时 ,Base1和Base2的vfptr呢?Son的vbptr呢? 在macOS的GCC编译器Clion平台下 ,准确来说 ,是64位系统下的GCC编译器中,子类父类会共享一个虚函数指针!!!,所以虚函数指针只有一个! csdn中的推荐链接 ,给了我很大的启发和帮助,感谢作者:https://blog.csdn.net/longjialin93528/article/details/79874558 问vbptr的去好好面壁思过(笑) ,只有虚拟继承才会出现虚基类指针 ,此时Son只有public继承,是不会存在虚基部指针的 。当然 ,如果是虚继承 ,那么自然会多出一个vbptr指针 。 内存对齐的问题
需要注意的是 ,不同编译器下的不同数据类型的大小是不同的 ,在64位系统下 ,我的macOS ,Clion ,GCC编译器下指针是8字节。为了使内存最大程度地被利用而且规范 ,会存在内存对齐的要求 。
以最长的虚继承的Son类型为例 ,大小应该是vbptr(8)+int(8)+vbptr(8)+int(4)+int(4)+vfptr(8)+int(8)= 48 。因为存在内存对齐,int这种4大小的类型 ,如果不能组合为8大小 ,将会由4扩至8,在Clion中编写 ,大小确实是48 ,证明推断并无错误。 总结
尽管类的内存结构很复杂,但是我们只要掌握这些最基本的结构知识 ,还是能够推断出大部分情况下的内存大小的 。
但是 ,不要为了所谓的学习 ,去故意写一些乱七八糟的继承 ,不仅从实际出发不实用 ,而且会让自己陷入陷阱中 ,在日常学习和编程中加以利用这些知识才是正途 。创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!