• 【深度探索C++对象模型】data语义学


    class X{};
    class Y :public virtual X{};
    class Z :public virtual X{};
    class A :public Y, public Z{};
    void main()
    {
        cout << sizeof(X) << " " << sizeof(Y) << " " << sizeof(Z) << " " << sizeof(A);
        system("pause");
    }

    vs2013的结果是1,4,4,8

    因为Y,Z继承自X,会有指针指向父类(虚继承)。该指针要么指向virtual base class subobject,要么指向一个相关表格,表格中存放的是virtual base class subobject的地址或偏移量(一般当当前class有多个直接virtual base class时)。

    X内部的1byte,使得这个class的多个object得以在内存中配置独一无二的地址。

    empty virtual base class 被视为derived class object 最开头的一部分,也就是说派生类有了member,那么就不需要原本因为empty class而插入的一个byte

    A为普通继承,所以是父类的和。

    一、data member的布局

    nonstatic data members在class object中的排列顺序将和其被声明的顺序一样,任何中间介入的static data member都不会被放进对象布局中。

    C++standard要求,在同一个access section中,member的排列只需符合较晚出现的member在class object中有较高的地址即可,各个member并不一定得连续排列。

    编译器可能会合成一些内部使用的data member,以支持整个对象模型,vptr就是这样的东西,当前所有的编译器都把它安插在每一个内含virtual function之class的object内,vptr的安放位置依不同的编译器决定。

    二、data member的存取

    1.static data member

    每一个static data member只有一个实体,存放在程序的data segment之中,并不再class object中,每次程序取用static member,就会被内部转化为对该唯一的extern实体的直接操作,也就是说通过一个指针和通过一个对象来存取static data member完全相同。

    通过对象存取static data member只是语法上的方便而已,static data member并不再class object中,因此存取static member 不需要通过class object。

    2.nonstatic data member

    Point3D origin,*pt=&origin;
    origin.x=0;
    pt->x=0;

    当Point3D是一个derived  class,而在其继承结构中有一个virtual base class,并且被存取的member是一个从该virtual base class继承而来的member时,上述两种方式有重大差异。这时候我们不知道pt指向哪一个class,所以这个存取操作必须延迟到程序运行时。但使用origin,member的offset位置在编译期间就固定了。

    欲对一个nonstatic data member进行存取操作,编译器需要把class object的起始地址加上data member的偏移量(offset)

     三、继承与data member

    1.只有继承无多态的情况

    class concrete1
    {
    public:
        //...
    private:
        int val;
        char bit1;
    };
    
    class concrete2:public concrete1
    {
    public :
        //..
    private:
        char bit2;
    };
    
    class concrete3 :public concrete2
    {
    public :
        //..
    private:
        char bit3;
    };
    
    void main()
    {
        cout << sizeof(concrete1) << " " << sizeof(concrete2) << " " << sizeof(concrete3);
        system("pause");
    }
    View Code

     输出值是8,12,16

     alignment的限制,使得类在内存中的存取更有效率,在32位计算机中,alignment通常为4bytes,刚好为一个word

     为何派生类成员不放在基类因为边界调整而多占的空间上呢?

    派生类对象拷贝给基类对象时,只有基类部分才会拷贝给基类对象,派生类部分被切割,如果不是按照上述的设计,就不能实现这一点

     (方向应该反过来看)

  • 相关阅读:
    用tcpdump 分析如何建立与关闭tcp连接 邓维 博客园
    Javascript基础与面向对象基础~Javascript中的语句如何被执行,如何改变执行顺序
    Javascript基础与面向对象基础~第一讲 啥叫编程,啥叫程序员
    将不确定变为确定~本机是否可以调试带域名的网站
    将不确定变为确定~为什么发布项目时用release环境更好些
    c/c++ 调用dos 命令
    C++基类与派生类的转换
    c++ max_elment和min_element
    c++ 无法链接的外部变量
    转:c++ 基类转换为派生类
  • 原文地址:https://www.cnblogs.com/ljygoodgoodstudydaydayup/p/3825484.html
Copyright © 2020-2023  润新知