飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 1858|回复: 2

[C/C++] 虚析构简单分析(关于菱形结构)

[复制链接]

该用户从未签到

发表于 2010-1-30 02:53:24 | 显示全部楼层 |阅读模式
构造:
B() ==> 父类构造 ==> 初始化列表(俺类内定义)==> 为虚表赋值  ==> 执行代码区
析构:
~B() ==> 先赋虚表 ==> 然后执行代码 ==> 析构(类)成员(对象) ==> 析构父类

============================

构造函数()
{
        父类构造
        初始化列表//const、引用、构造成员对象
        为虚表赋值
        执行代码
};

析构函数()
{
        先赋虚表
        执行代码
        析构成员对象
        析构父类
};

============================

new()
{
}

delete()
{
}

============================

new[]()
{
}

delete[]()
{
}

============================

虚继承

9:        D thed;
004010E8   push        1
004010EA   lea         ecx,[ebp-14h]
004010ED   call        @ILT+30(D::D) (00401023)
10:       return 0;
004010F2   mov         dword ptr [ebp-18h],0
004010F9   lea         ecx,[ebp-14h]
004010FC   call        @ILT+35(D::`vbase destructor') (00401028)
00401101   mov         eax,dword ptr [ebp-18h]
11:   }

//

EBP-14H    1
EBP-10H    this
EBP-04H     0
EBP+8       0

D::`vbtable' (00425060)
D::`vbtable' (00425054)
0
xx  (A::A) (0040102d)
xx

0040167F   mov         dword ptr [ebp-10h],ecx
00401682   mov         dword ptr [ebp-14h],0
00401689   cmp         dword ptr [ebp+8],0
0040168D   je          D::D+6Dh (004016bd)
// 先赋了两张虚表值
0040168F   mov         eax,dword ptr [ebp-10h]
00401692   mov         dword ptr [eax],offset D::`vbtable' (00425060)
00401698   mov         ecx,dword ptr [ebp-10h]
0040169B   mov         dword ptr [ecx+4],offset D::`vbtable' (00425054)

//
00425060
00425054
CCCCCCCC
CCCCCCCC ==> 0042502c
CCCCCCCC     00382948

004016A2   mov         ecx,dword ptr [ebp-10h]
004016A5   add         ecx,0Ch
004016A8   call        @ILT+40(A::A) (0040102d)   // 先构造祖父

004016AD   mov         edx,dword ptr [ebp-14h]  // 0
004016B0   or          edx,1
004016B3   mov         dword ptr [ebp-14h],edx  // 0
004016B6   mov         dword ptr [ebp-4],0

004016BD   push        0
004016BF   mov         ecx,dword ptr [ebp-10h]
004016C2   call        @ILT+25(B::B) (0040101e)    //  B类构造时传入的就是 this 指针
004016C7   mov         dword ptr [ebp-4],1

004016CE   push        0
004016D0   mov         ecx,dword ptr [ebp-10h]
004016D3   add         ecx,4
004016D6   call        @ILT+10(CC::CC) (0040100f)  //  CC类构造时传入的是 (this + 4) 指针

004016DB   mov         eax,dword ptr [ebp-10h]
004016DE   mov         ecx,dword ptr [eax]
004016E0   mov         edx,dword ptr [ecx+4]
004016E3   mov         eax,dword ptr [ebp-10h]
004016E6   mov         dword ptr [eax+edx],offset D::`vftable' (00425050)  // 赋值自己的虚表
14:       m_d = 30;
004016ED   mov         ecx,dword ptr [ebp-10h]
004016F0   mov         dword ptr [ecx+8],1Eh
15:   }

// 从流程上看 虚继承就是多了一个先赋值两个父类虚表的流程

//
00425060
00425054
CCCCCCCC         B            CC          D
0042502c ==> 00425030 ==> 00425040 ==> 00425050
00382948


B::B() 的构造

0040130A   mov         dword ptr [ebp-4],ecx
0040130D   mov         dword ptr [ebp-8],0
00401314   cmp         dword ptr [ebp+8],0  // 压入的数据为0
00401318   je          B::B+47h (00401337)

//------------------------------------------
0040131A   mov         eax,dword ptr [ebp-4]
0040131D   mov         dword ptr [eax],offset B::`vbtable' (00425034)     // 修改 D对象 第一项数据

00401323   mov         ecx,dword ptr [ebp-4]
00401326   add         ecx,4                             // 为什么要加4呢 他为什么要做两张虚表呢 ?       
00401329   call        @ILT+40(A::A) (0040102d)     // A 类的构造为 (this+4)的地址

0040132E   mov         ecx,dword ptr [ebp-8]  // 0
00401331   or          ecx,1                 
00401334   mov         dword ptr [ebp-8],ecx  // 1
00401337   mov         edx,dword ptr [ebp-4]
------------------------------------------//

0040133A   mov         eax,dword ptr [edx]     // 取虚表第一项
0040133C   mov         ecx,dword ptr [eax+4]   // 虚表之后的一个数据
0040133F   mov         edx,dword ptr [ebp-4]
00401342   mov         dword ptr [edx+ecx],offset B::`vftable' (00425030)  // 再次赋值赋值派生类的虚表
14:
15:   }

==> 00425060
//  
00000000
0C000000

00425060
00425054
CCCCCCCC
0042502c ==> 00425030
00382948   

CC::CC() 的构造

004014BA   mov         dword ptr [ebp-4],ecx
004014BD   mov         dword ptr [ebp-8],0
004014C4   cmp         dword ptr [ebp+8],0
004014C8   je          CC::CC+47h (004014e7)

//------------------------------------------
004014CA   mov         eax,dword ptr [ebp-4]
004014CD   mov         dword ptr [eax],offset CC::`vbtable' (00425044)   // 修改 D对象 的第二项

004014D3   mov         ecx,dword ptr [ebp-4]
004014D6   add         ecx,4
004014D9   call        @ILT+40(A::A) (0040102d)

004014DE   mov         ecx,dword ptr [ebp-8]   // 1
004014E1   or          ecx,1
004014E4   mov         dword ptr [ebp-8],ecx
------------------------------------------//

004014E7   mov         edx,dword ptr [ebp-4]
004014EA   mov         eax,dword ptr [edx]
004014EC   mov         ecx,dword ptr [eax+4]
004014EF   mov         edx,dword ptr [ebp-4]
004014F2   mov         dword ptr [edx+ecx],offset CC::`vftable' (00425040)  // 再次赋值赋值派生类的虚表
14:

// 00425054
==>
00000000
08000000

00425060
00425054
CCCCCCCC       B            CC  
0042502c ==> 00425030 ==> 00425040
00382948

//
00425054  00 00 00 00  .... -->
00425058  08 00 00 00  ....
0042505C  00 00 00 00  ....
00425060  00 00 00 00  .... -->
00425064  0C 00 00 00  ....
00425068  00 00 00 00  


//  注意哦 B类构造 和 CC类构造 的一个数据

00425034
00425044

// 这里不仅保存了虚表地址 还保留了父类的偏移地址

00425030  0A 10 40 00  ..@.  ---->  B  类虚表地址
00425034  00 00 00 00  ....
00425038  04 00 00 00  ....  //
0042503C  00 00 00 00  ....

00425040  32 10 40 00  2.@.  ---->  CC  类虚表地址
00425044  00 00 00 00  ....
00425048  04 00 00 00  ....  //
0042504C  00 00 00 00  ....

00425050  64 10 40 00  .... ---->   D 类虚表地址 (虚表后是一个 DWORD[3] 的结构体哦)
// 果然是虚表第一项 00401064

00425054  00 00 00 00  ....  //   虚表第二数值  CC 类构造时 相对于 A 类的偏移地址
00425058  08 00 00 00  ....
0042505C  00 00 00 00  ....

00425060  00 00 00 00  ....  ---->
00425064  0C 00 00 00  ....       虚表第一数值  B 类构造时 相对于 A 类的偏移地址
00425068  00 00 00 00  ....

MOV EDX,[EBP-4]
MOV EAX,[EAX+4]
MOV ECX,[EAX+4]  // 这里如果是自己构造则为 4
MOV EDX,[EBP-4]

[EDX + ECX]

==> 构造完成后 都要修改父类的虚表地址

D 类的虚继承方案

1. 先赋值两张数据
2. 然后构造 祖父类
3. 然后再构造 B 和 CC 类时压入参数 0
B 和 CC 中的构造判断 参数 = 0
则跳过 A 类的构造
自己赋值虚表后,然后修改派生类的虚表
4. 赋值自己的虚表

// ====================================================================

析构函数:

0040114A   mov         dword ptr [ebp-4],ecx
0040114D   mov         ecx,dword ptr [ebp-4]
00401150   add         ecx,0Ch                        // 这里找到 A 类的地址传入析构函数
00401153   call        @ILT+65(D::~D) (00401046)
00401158   mov         ecx,dword ptr [ebp-4]
0040115B   add         ecx,0Ch                        // 这里找到 A 类的地址传入析构函数       
0040115E   call        @ILT+55(A::~A) (0040103c)

D::~D

004017EF   mov         dword ptr [ebp-10h],ecx
004017F2   mov         eax,dword ptr [ebp-10h]
004017F5   mov         ecx,dword ptr [eax-0Ch]
004017F8   mov         edx,dword ptr [ecx+4]
004017FB   mov         eax,dword ptr [ebp-10h]
004017FE   mov         dword ptr [eax+edx-0Ch],offset D::`vftable' (00425050)
00401806   mov         dword ptr [ebp-4],0
19:
20:   }
0040180D   mov         ecx,dword ptr [ebp-10h]
00401810   sub         ecx,0Ch
00401813   test        ecx,ecx
00401815   je          D::~D+62h (00401822)
00401817   mov         edx,dword ptr [ebp-10h]
0040181A   sub         edx,8
0040181D   mov         dword ptr [ebp-14h],edx
00401820   jmp         D::~D+69h (00401829)      
00401822   mov         dword ptr [ebp-14h],0
00401829   mov         ecx,dword ptr [ebp-14h]
0040182C   add         ecx,4
0040182F   call        @ILT+80(CC::~CC) (00401055)      //  反顺序先析构 CC
00401834   mov         dword ptr [ebp-4],0FFFFFFFFh
0040183B   mov         ecx,dword ptr [ebp-10h]
0040183E   sub         ecx,8
00401841   call        @ILT+60(B::~B) (00401041)       //  //  反顺序先析构 B
00401846   mov         ecx,dword ptr [ebp-0Ch]

D::~D  `vbase destructor' (调用自己的虚表第一项)
析构自身,自身的析构去调用其继承的父类 ( CC 和 B )
而所继承的父类 他们虚继承了 A 类,所以在 CC 和 B 类的析构中不对A类做任何处理
然后再去析构 A 类
PYG19周年生日快乐!
  • TA的每日心情
    开心
    2023-8-23 14:44
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-1-31 22:52:21 | 显示全部楼层
    /:002 /:002   这还叫简单分析啊
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-2-6 00:07:23 | 显示全部楼层
    提示: 作者被禁止或删除 内容自动屏蔽
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表