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