C++代码分析(二)

2014-11-24 12:50:51 · 作者: · 浏览: 2
8B50 08 MOV EDX,DWORD PTR DS:[EAX+8]
004010CF |. 8BCE MOV ECX,ESI
004010D1 |. FFD2 CALL EDX
这里ESI指向TestA类的起始地址,把这个起始地址传到EAX里面之后,就把这个类里面的一个函数地址放到EDX里面,TestA类本身一共有4个函数,刚才构造函数被外部也就是Main调用了,那么里面只剩下3个地址了,我们知道一个类如果有虚析构函数,第一个地址就指向虚析构函数的地址,EAX+8就是调用了
pTest->pointf();至于为什么,自己想一下,MOV ECX,ESI通过ECX来保证堆栈的平衡
004010D3 |. 8B06 MOV EAX,DWORD PTR DS:[ESI] ; C++构造.0040218C
004010D5 |. 8B50 04 MOV EDX,DWORD PTR DS:[EAX+4]
004010D8 |. 8BCE MOV ECX,ESI
004010DA |. FFD2 CALL EDX
这里就调用了pTest->prointer();因为我们是根据类的地址来决定调用哪个函数的
004010DC |. 8B06 MOV EAX,DWORD PTR DS:[ESI]
004010DE |. 8B10 MOV EDX,DWORD PTR DS:[EAX] ; C++构造.00401050
004010E0 |. 6A 01 PUSH 1
004010E2 |. 8BCE MOV ECX,ESI
004010E4 |. FFD2 CALL EDX
这里就是调用TestA类的虚折构函数,也就是当前类的地址的第一个指针,我们跟踪进去看一下,下面是反汇编代码:
00401050 . 56 PUSH ESI
00401051 . 57 PUSH EDI
00401052 . 8B3D B0204000 MOV EDI,DWORD PTR DS:[<&MSVCR90.printf>] ; msvcr90.printf
00401058 . 8BF1 MOV ESI,ECX
0040105A . 68 2C214000 PUSH 0040212C ; /format = "TestA::TestA"
0040105F . C706 8C214000 MOV DWORD PTR DS:[ESI],0040218C ; |
00401065 . FFD7 CALL EDI ; \printf
00401067 . 68 18214000 PUSH 00402118 ; ASCII "Virtual ~Test()"
0040106C . C706 7C214000 MOV DWORD PTR DS:[ESI],0040217C
00401072 . FFD7 CALL EDI
00401074 . 83C4 08 ADD ESP,8
00401077 . F64424 0C 01 TEST BYTE PTR SS:[ESP+C],1
0040107C . 74 09 JE SHORT 00401087
0040107E . 56 PUSH ESI
0040107F . E8 76000000 CALL
00401084 . 83C4 04 ADD ESP,4
00401087 > 5F POP EDI
00401088 . 8BC6 MOV EAX,ESI
0040108A . 5E POP ESI
0040108B . C2 0400 RETN 4
这里调用了两个虚析构函数的地方,为什么是先调用~TestA,而不是先调用~Test呢,因为我们把这两个析构函数定义为了虚函数,虚函数是在运行期决定调用谁的,当我们把TestA的成员函数调用完毕之后,析构函数会自动调用,因此,TestA完了之后就调用自己的析构函数,释放最新分配的内存,所以,先调用TestA的折构函数,再调用Test的折构函数,这也是为什么把析构函数声明为虚函数的原因,
这里调用了两个虚析构函数之后,就是用delete指针删除了由new分配的地址,分析完毕。
总结一下:
当我们定义了一个带有虚函数的类的时候,这个类的虚函数就会被放在一个地址表里面,这个地址表被放在类的入口里面,当我们调用哪个类的时候,就使用哪个类的入口来调用里面的虚函数,这就证名明了C++中得函数同名的虚函数的多态机制。
讲得不好,如果有错误的地方请各位指出来,谢谢!


作者“科技改变世界”