zoukankan      html  css  js  c++  java
  • 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++不久,难免有错误,见谅。
  • 相关阅读:
    155. 最小栈
    160. 相交链表
    PAT 1057 Stack
    PAT 1026 Table Tennis
    PAT 1017 Queueing at Bank
    PAT 1014 Waiting in Line
    PAT 1029 Median
    PAT 1016 Phone Bills
    PAT 1010 Radix
    PAT 1122 Hamiltonian Cycle
  • 原文地址:https://www.cnblogs.com/fancystar/p/6017869.html
Copyright © 2011-2022 走看看