• C++派生类的成员内存布局


     
    class A {};
    class B : public virtual A {};
    class C : public virtual A {};
    class D : public B, public C {};
     
    int main()
    {
           A a;
           B b;
           C c;
           D d;
           cout << sizeof(a) << endl;
           cout << sizeof(b) << endl;
           cout << sizeof(c) << endl;
           cout << sizeof(d) << endl;
           getchar();
           return 0;
    }
    class A {};
    class B : public virtual A {};
    class C : public virtual A {};
    class D : public B, public C {};
     
    int main()
    {
           A a;
           B b;
           C c;
           D d;
           cout << sizeof(a) << endl;
           cout << sizeof(b) << endl;
           cout << sizeof(c) << endl;
           cout << sizeof(d) << endl;
           getchar();
           return 0;
    }
    得到结果:
    1
    4
    4
    8
     
     
    mov         eax,dword ptr [this] 
    mov         dword ptr [eax],offset B::`vbtable' (0C46B30h)  //b对象内存中只有一个虚表指针
     
     
    mov         eax,dword ptr [this] 
    mov         dword ptr [eax],offset D::`vbtable' (0C46B48h) 
    mov         eax,dword ptr [this] 
    mov         dword ptr [eax+4],offset D::`vbtable' (0C46B54h)  //d对象内存中有连个虚表指针
     
     
    非静态成员的赋值:
     
    虚继承时:
    并无差别
     
     
    ——————————————————————————————————————————————————
     小插曲:
    对于子类对象复制给基类指针时发生覆盖的验证:
    可能发生在早期版本。
     
    class base
    {
    int val;
    char a;
    }
     
    class derived : public base
    {
    char b;
    }
     
    derived * p2;
    base* p1_1,*p1_2;
     
    p1_1=p2;
    *p1_2=*p1_1;
    所谓发生members内容覆盖:
     
     
     
     
    测试:
     
    int main()
    {
           char *mem;
           A a;
           B b;
           C c;
           A *pa1, *pa2 = new A;
           B *pb=new B;
           pa2->str1 = 'A';
           pb->str2 = 'B';
           pb->a = 2;
           pb->str1 = 'b';
           pa1 = pb;
           *pa2 = *pa1;
           mem = (char*)pa1;
           HexDump(mem, 16, (int)mem);
           mem = (char*)pa2;
           HexDump(mem, 16, (int)mem);
           getchar();
           return 0;
    }
    我们得到:
     
    尽管基类指针指向派生类对象,但是静态内存空间大小仍为基类大小。
    静态绑定与动态绑定,参考:http://www.cnblogs.com/lizhenghn/p/3657717.html
    基类指针无法引用派生类的数据成员。
    在*pa2 = *pa1;体现,copy只发生了sizeof(a)。‘B’未被复制。所以派生类成员被指定未知值的行为并不会发生。
     
    ——————————————————————————————————————————————————
     
     
     
    具体继承:
     
     
     
    多重继承:
     
    class Point2d
    {
    public:
           virtual void fun1() {};
    protected:
           float x_, y_;
    };
     
    class Point3d :public Point2d
    {
    protected:
           float z_;
    };
     
    class Vertex
    {
    public:
           virtual void fun2() {};
    protected:
           Vertex * next;
    };
     
    class Vertex3d :public Point3d, public Vertex
    {
    protected:
           float mumble;
    };
     
    int main()
    {
           Vertex3d v3d;
           Vertex3d * pv3d = &v3d;
           Vertex *pv=new Vertex;
           Point2d *p2d =new Point2d;
           Point3d *p3d= new Point3d;
           pv = &v3d;
           p2d = &v3d;
           p3d = &v3d;
           printf("%p
    ",&v3d);
           getchar();
           return 0;
    }
     
    内存布局:
    *pv:
    010262E0 | 44 AB EA 00 | 00 00 00 00 |
     
     
    *p2d:
    010264D8 | 34 AB EA 00 | CD CD CD CD | CD CD CD CD |
     
     
     
    *p3d:
    0101EF28 | 3C AB EA 00 | CD CD CD CD | CD CD CD CD | CD CD CD CD |  
     
     
    *pv3d:
    00D3FA3C | 4C AB D6 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 54 AB D6 00 | 00 00 00 00 | 00 00 00 00 |
     
     
     
     
     
     
           pv = &v3d;
           p2d = &v3d;
           p3d = &v3d;
    不同的赋值方式:
     
           pv = &v3d;
    00EA1E24 8D 45 DC             lea         eax,[v3d] 
    00EA1E27 85 C0                test        eax,eax 
    00EA1E29 74 0E                je          main+119h (0EA1E39h) 
    00EA1E2B 8D 4D DC             lea         ecx,[v3d] 
    00EA1E2E 83 C1 10             add         ecx,10h                        //&v3d+sizeof(Point3d)
    00EA1E31 89 8D CC FE FF FF    mov         dword ptr [ebp-134h],ecx 
    00EA1E37 EB 0A                jmp         main+123h (0EA1E43h) 
    00EA1E39 C7 85 CC FE FF FF 00 00 00 00 mov         dword ptr [ebp-134h],0 
    00EA1E43 8B 95 CC FE FF FF    mov         edx,dword ptr [ebp-134h] 
    00EA1E49 89 55 D0             mov         dword ptr [pv],edx            //pv=(Vertex*)(&v3d+sizeof(Point3d))
           p2d = &v3d;
    00EA1E4C 8D 45 DC             lea         eax,[v3d] 
    00EA1E4F 89 45 C4             mov         dword ptr [p2d],eax           //p2d=&v3d
           p3d = &v3d;
    00EA1E52 8D 45 DC             lea         eax,[v3d] 
    00EA1E55 89 45 B8             mov         dword ptr [p3d],eax           //p3d=&v3d
     
     
    由之前所诉的pv指针仍指向大小为sizeof(Vertex)的内存区域,所以&v3d所加的偏移也就理所应当了。
     
     
     
    虚拟继承:
     
    class Point2d
    {
    public:
           virtual void fun1() {};
    protected:
           float x_, y_;
    };
     
    class Point3d :public virtual Point2d
    {
    public:
           void operator+=(const Point3d &rhs)
           {
                  x_ += rhs.x_;
                  y_ += rhs.y_;
                  z_ += rhs.z_;
           }
    protected:
           float z_;
    };
     
    class Vertex:public virtual Point2d
    {
    public:
           virtual void fun2() {};
    protected:
           Vertex * next;
    };
     
    class Vertex3d :public Vertex,public Point3d
    {
    protected:
           float mumble;
    };
     
    int main()
    {
           Vertex3d v3d;
           Vertex3d * pv3d = &v3d;
           Vertex *pv = new Vertex;
           Point2d *p2d = new Point2d;
           Point3d *p3d = new Point3d;
           p2d = pv3d;
           printf("%p
    ", &v3d);
           getchar();
           return 0;
    }
     
    *p3d:
    0101CEA8 | 40 AB 3C 00 | CD CD CD CD | 3C AB 3C 00 | CD CD CD CD | CD CD CD CD |
     
    *pv:
    01023D80 | 4C AB 3C 00 | 58 AB 3C 00 | 00 00 00 00 | 54 AB 3C 00 | 00 00 00 00 | 00 00 00 00 |
     
    *pv3d:
     
    00DAF7F0 | 64 AB 3C 00 | 70 AB 3C 00 | 00 00 00 00 | 78 AB 3C 00 | 00 00 00 00 | 00 00 00 00 | 6C AB 3C 00 | 00 00 00 00 | 00 00 00 00|
     
     
     
     
    继承顺序:先具体继承后虚拟继承,先左后右。
    一个类对象的内存层次:
    本类中有虚函数,则顶部为虚函数表指针;
    先具体继承:
       基类无继承,则放置基类成员变量
       基类具体继承,按照本规则递归
       基类虚继承,有虚函数,则放置虚函数指针,后接虚基类表指针、无虚函数,则放置虚基类表指针;(类与基类公用虚基类表指针)
    后虚拟继承:
       基类无继承,则放置基类成员变量
       基类具体继承,按照上规则递归
       基类虚继承,先基类后子类,按照本规则递归
    成员变量;
    虚基类内存区域;
     
     
     
     
     
     
    vbptr虚基类表指针
    很不错,比我讨论的更详细:http://blog.csdn.net/chengonghao/article/details/51736585
    以Point3d举例:
    *p3d:
    0101CEA8 | 40 AB 3C 00 | CD CD CD CD | 3C AB 3C 00 | CD CD CD CD | CD CD CD CD |
    虚基类表指针地址:
    003CAB40 | 00 00 00 00 | 08 00 00 00 |
    类内存起始距离虚基类表指针为0
    class Point3d虚继承的基类Point2d内存起始距离虚基类表指针为8
     
     
    *pv:
    01023D80 | 4C AB 3C 00 | 58 AB 3C 00 | 00 00 00 00 | 54 AB 3C 00 | 00 00 00 00 | 00 00 00 00 |
     
    003CAB58 | FC FF FF FF | 08 00 00 00 |
    类内存起始距离虚基类表指针为-4
    class Vertex虚继承的基类Point2d内存起始距离虚基类表指针为8
     
    *pv3d:
    00DAF7F0 | 64 AB 3C 00 | 70 AB 3C 00 | 00 00 00 00 | 78 AB 3C 00 | 00 00 00 00 | 00 00 00 00 | 6C AB 3C 00 | 00 00 00 00 | 00 00 00 00|
     
    003CAB70 | FC FF FF FF | 14 00 00 00 |
    基类Vertex内存起始距离虚基类表指针为-4
    class Vertex3d继承的class Vertex虚继承的基类Point2d内存起始距离虚基类表指针为20
    003CAB78 | 00 00 00 00 | 0C 00 00 00 |
    基类Point3d内存起始距离虚基类表指针为0
    class Vertex3d继承的class Point3d虚继承的基类Point2d内存起始距离虚基类表指针为12
     
     
     
     
     
    思路参考《深度探索C++对象模型》,由于书中C++版本较老,许多规已经不适用了,纠正了部分。
    由于我学习C++不久,难免有错误,见谅。
  • 相关阅读:
    前端发展态势 && 前端工作流程个人浅析
    Mac在Django安装mysqlclient时报错
    rabbitMQ简单配置及OSError: [Errno 9] Bad file descriptor问题
    MacOS 出现command not found
    Celery简单说明以及在Django中的配置
    Celery目录结构配置
    shell脚本之安装docker
    阿里云按需购买设置
    Java中的关键字 transient
    jvm常用参数
  • 原文地址:https://www.cnblogs.com/fancystar/p/6017869.html
Copyright © 2020-2023  润新知