首页 > 代码库 > C++派生类的成员内存布局

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\n",&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\n", &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++不久,难免有错误,见谅。

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