菱形继承引发的问题和解决方案,以及底层实现的原理.
2017-03-26 16:38
531 查看
定义:两个子类继承同一个父类,而又有子类同时继承这两个子类。
如果直接继承会引发访问不明确(二义性),以及数据冗余。如果直接指定访问对象,可解决二义性(第一段代码以及解析图),而要解决数据冗余,则要引入虚函数(第二段代码以及解析图)。
代码一:
由上图可以看出,继承顺序为C
B , D的继承方式决定创建空间的先后。指定访问对象可以解决二义性,却无法解决数据冗余。
代码二:
对比上图,由于vptr的存在,空间加大,解决了数据冗余的问题。下面为实现过程
现在我们查看dd对象创建的过程。
1.进入反汇编
D dd;
010B4BC8 push
1
010B4BCA lea
ecx,[dd]
010B4BCD call
D::D (010B1442h)
//dd创建
dd.B::_a = 1;
010B4BD2 mov
eax,dword ptr [dd]
010B4BD5 mov
ecx,dword ptr [eax+4]
010B4BD8 mov
dword ptr dd[ecx],1
//写入1
dd._b = 3;
010B4BE0 mov
dword ptr [ebp-20h],3
dd._b1 = 6;
010B4BE7 mov
dword ptr [ebp-1Ch],6
dd.C::_a = 2;
010B4BEE mov
eax,dword ptr [dd]
010B4BF1 mov
ecx,dword ptr [eax+4]
010B4BF4 mov
dword ptr dd[ecx],2
dd._c = 4;
010B4BFC mov
dword ptr [ebp-14h],4
dd._d = 5;
010B4C03 mov
dword ptr [ebp-10h],5
return 0;
2.进入dd创建语句
D::D:(部分语句)
//之前语句对dd进行了初始化。
010B2D47 je
D::D+3Ch (010B2D5Ch)
010B2D49 mov
eax,dword ptr [this]
//将dd首地址送往eax.
010B2D4C mov
dword ptr [eax],10BCCCCh //将B距离A的偏移地址
//送到eax.
010B2D52 mov
eax,dword ptr [this]
010B2D55 mov
dword ptr [eax+0Ch],10BCCE8h //将C距离A的地址
//送到eax.
010B2D5C push
0
010B2D5E mov
ecx,dword ptr [this]
010B2D61 call
B::B (010B143Dh)
//B的构造
010B2D66 push
0
010B2D68 mov
ecx,dword ptr [this]
010B2D6B add
ecx,0Ch
//将ecx+B的大小
010B2D6E call
C::C (010B1447h)
//C的构造
010B2D73 mov
eax,dword ptr [this]
进入B的构造(部分)
010B3049 mov
eax,dword ptr [this]
010B304C mov
dword ptr [eax],10BCC70h
//存放B的大小
010B3052 mov eax,dword ptr [this]
进入C的构造部分
010B3119 mov
eax,dword ptr [this]
010B311C mov
dword ptr [eax],10BCCB8h //存放C的大小
010B3122 mov eax,dword ptr [this]
dd对象在创建的过程中,调用D的构造,然后对B和C进构造,在B构造时,在其起始位置(dd位置)放置一指向B的父类(A)的指针的偏移地址,偏移地址+4便是距离A的偏移量,同时在创建一偏移地址,保存B的对象的大小。然后dd加上B的对象的空间大小,在之后创建B的对象。B和A的对象的首地址指向同一空间(父类A)。
ps:对于偏移地址当前内容是为其他偏移量预留,在菱形继承和多态进行共同探究时发现那个位置存放的是距离this的偏移量。
如果直接继承会引发访问不明确(二义性),以及数据冗余。如果直接指定访问对象,可解决二义性(第一段代码以及解析图),而要解决数据冗余,则要引入虚函数(第二段代码以及解析图)。
代码一:
#include <iostream> using namespace std; class A { public: int _a; }; class B : public A { public: int _b; }; class C : public A { public: int _c; }; class D : public C, public B { public: int _d; }; int main() { D dd; dd.C::_a =2; dd._c = 4; dd.B::_a =1; dd._b = 3; dd._d = 5; return 0; }
由上图可以看出,继承顺序为C
B , D的继承方式决定创建空间的先后。指定访问对象可以解决二义性,却无法解决数据冗余。
代码二:
#include <iostream> using namespace std; class A { public: int _a; }; class B : virtual public A { public: int _b; int _b1; }; class C : virtual public A { public: int _c; }; class D : public B, public C { public 4000 : int _d; }; int main() { D dd; dd.B::_a =1; dd._b = 3; dd._b1 = 6; dd.C::_a =2; dd._c = 4; dd._d = 5; return 0; }
对比上图,由于vptr的存在,空间加大,解决了数据冗余的问题。下面为实现过程
现在我们查看dd对象创建的过程。
1.进入反汇编
D dd;
010B4BC8 push
1
010B4BCA lea
ecx,[dd]
010B4BCD call
D::D (010B1442h)
//dd创建
dd.B::_a = 1;
010B4BD2 mov
eax,dword ptr [dd]
010B4BD5 mov
ecx,dword ptr [eax+4]
010B4BD8 mov
dword ptr dd[ecx],1
//写入1
dd._b = 3;
010B4BE0 mov
dword ptr [ebp-20h],3
dd._b1 = 6;
010B4BE7 mov
dword ptr [ebp-1Ch],6
dd.C::_a = 2;
010B4BEE mov
eax,dword ptr [dd]
010B4BF1 mov
ecx,dword ptr [eax+4]
010B4BF4 mov
dword ptr dd[ecx],2
dd._c = 4;
010B4BFC mov
dword ptr [ebp-14h],4
dd._d = 5;
010B4C03 mov
dword ptr [ebp-10h],5
return 0;
2.进入dd创建语句
D::D:(部分语句)
//之前语句对dd进行了初始化。
010B2D47 je
D::D+3Ch (010B2D5Ch)
010B2D49 mov
eax,dword ptr [this]
//将dd首地址送往eax.
010B2D4C mov
dword ptr [eax],10BCCCCh //将B距离A的偏移地址
//送到eax.
010B2D52 mov
eax,dword ptr [this]
010B2D55 mov
dword ptr [eax+0Ch],10BCCE8h //将C距离A的地址
//送到eax.
010B2D5C push
0
010B2D5E mov
ecx,dword ptr [this]
010B2D61 call
B::B (010B143Dh)
//B的构造
010B2D66 push
0
010B2D68 mov
ecx,dword ptr [this]
010B2D6B add
ecx,0Ch
//将ecx+B的大小
010B2D6E call
C::C (010B1447h)
//C的构造
010B2D73 mov
eax,dword ptr [this]
进入B的构造(部分)
010B3049 mov
eax,dword ptr [this]
010B304C mov
dword ptr [eax],10BCC70h
//存放B的大小
010B3052 mov eax,dword ptr [this]
进入C的构造部分
010B3119 mov
eax,dword ptr [this]
010B311C mov
dword ptr [eax],10BCCB8h //存放C的大小
010B3122 mov eax,dword ptr [this]
dd对象在创建的过程中,调用D的构造,然后对B和C进构造,在B构造时,在其起始位置(dd位置)放置一指向B的父类(A)的指针的偏移地址,偏移地址+4便是距离A的偏移量,同时在创建一偏移地址,保存B的对象的大小。然后dd加上B的对象的空间大小,在之后创建B的对象。B和A的对象的首地址指向同一空间(父类A)。
ps:对于偏移地址当前内容是为其他偏移量预留,在菱形继承和多态进行共同探究时发现那个位置存放的是距离this的偏移量。
相关文章推荐
- c++:继承相关的要点热点,以及菱形继承的底层实现
- hashmap底层实现原理以及常见的面试问题
- IOS-SDWebImage 底层实现原理以及面试题相关问题的学习链接
- Multidex机制,以及实现原理,产生的问题和解决方案
- spring boot 拦截器的实现以及遇到的问题的解决方案
- 在Android中实现推送方式的底层原理与推送的知识及相关解决方案
- 深入剖析ThreadLocal实现原理以及内存泄漏问题
- Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题
- Hashtable、HashMap、ConcurrentHashMap 底层实现原理与线程安全问题
- Hashtable,HashMap,ConcurrentHashMap 底层实现原理与线程安全问题
- 使用Spring MVC 实现文件上传遇到的问题以及解决方案
- Linux管理工作,实例讲解工作中使用ssh证书登录的实际流程,讲解ssh证书登录的配置原理,基于配置原理,解决实际工作中,windows下使用SecureCRT证书登录的各种问题,以及实现hadoo
- session共享原理以及PHP 实现多网站共享用户SESSION 数据解决方案
- 解析php中session的实现原理以及大网站应用应注意的问题
- 底层解惑-spring的@service的原理及其一接口多实现的解决方案
- ListView中子view复用机制的实现原理以及图片错位的解决方案
- 多继承实现COM类中的"菱形问题" C++也有一样的菱形问题
- handler的原理以及底层的实现
- 深入剖析ThreadLocal实现原理以及内存泄漏问题
- Mysql的事务和事务的隔离级别,读写引发的安全问题以及解决方案