您的位置:首页 > 编程语言 > C语言/C++

菱形继承引发的问题和解决方案,以及底层实现的原理.

2017-03-26 16:38 531 查看
        定义:两个子类继承同一个父类,而又有子类同时继承这两个子类。
        如果直接继承会引发访问不明确(二义性),以及数据冗余。如果直接指定访问对象,可解决二义性(第一段代码以及解析图),而要解决数据冗余,则要引入虚函数(第二段代码以及解析图)。
 
代码一:

#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       

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       

010B2D5E mov        
ecx,dword ptr [this] 
010B2D61 call       
B::B (010B143Dh)                           
//B的构造
010B2D66 push       

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++ 菱形继承
相关文章推荐