00401E67 mov ecx,dword ptr [ebp-10h]
00401E6A add ecx,4
00401E6D call CString::operator char const * (0040214c)
00401E72 mov esi,esp
00401E74 push eax
00401E75 call dword ptr [__imp__OutputDebugStringA@4 (004173e0)]
00401E7B cmp esi,esp
00401E7D call _chkesp (004021ca)
00401E82 mov dword ptr [ebp-4],0FFFFFFFFh
00401E89 mov eax,dword ptr [ebp-10h]
00401E8C mov ecx,dword ptr [ebp-0Ch]
00401E8F mov dword ptr fs:[0],ecx
00401E96 pop edi
00401E97 pop esi
00401E98 pop ebx
00401E99 add esp,50h
00401E9C cmp ebp,esp
00401E9E call _chkesp (004021ca)
00401EA3 mov esp,ebp
00401EA5 pop ebp
00401EA6 ret
因为代码较多,在此只选择其中关键部分的代码来讨论这里把构造函数的整代码分成3部分函数初始化代码、类初始化代码、函数体代码。
(1)函数初始化代码
首先来简单了解一下00401DF0到00401E1C的这段代码,主要是一些保存当前状态的操作,大家可以看到最后一个入栈的寄存器ECX,那也就是说代码一直执行00401E1C处 ECX都是栈顶第一个元素。下面代码接着是00401E1E位置的代码,它把栈顶的一个元素弹出并保存到了ECX寄存器中,这个寄存器中当前值是什么呢?可能大家都一直这个疑问,返回再去看看构造函数调用前的那一句汇编代码:
00401D96 mov ecx,dword ptr [ebp-28h]
这样大家应该明白了,ECX寄存器中存放的就是CMemoryItem对象的起始地址,这时它被保存入dword ptr [ebp-10h] 内存区。
(2)类初始化代码
这个阶段从00401E1E一直到00401E37,在这段汇编代码中间没有看到有关在类的构造函数中写的操作。它首先把对象的起始地址存入了dword ptr [ebp-10h],然后将对象指针偏移4个字节,因为这个地方存放了类的成员变量m_info;由于它是CString类的对象所以这里调用了它的默认构造函数来构造这个成员变量。接着再看00401E34和00401E37这两句汇编代码;先把对象的起始地址放到EAX寄存器中,然后把offset CMemoryItem::`vftable' (0041501c)放入对象前4个字节中,这里大家肯定有一个疑问那就是这个偏移地址到底是什么?再返回去看CMemoryItem类的声明,可以看到它的构造函数被声明成了一个虚函数。一些编译器支持虚函数的方法就是为这个类建立一个虚函数表,可见这个偏移就是这个虚函数表了。
|