標籤:
C++成員不通過對象調用(.或->方式)的另類(C式)調用寫法
#include <iostream>using namespace std;/* 我們知道,成員函數和普通函數最大的區別就是成員函數包含一個隱藏的參數this指標,用來表明成員函數當前作用在那一個對象執行個體上。根據呼叫慣例(Calling Convention)的不同,成員函數實現this指標的方式也不同。1. 如果使用__thiscall呼叫慣例,那麼this指標儲存在寄存器ECX中,VC編譯器預設情況下就是這樣的。2. 如果是__stdcall或__cdecl呼叫慣例,this指標將通過棧進行傳遞,且this指標是最後一個被壓入棧的參數,相當於編譯器在函數的參數列表中最左邊增加了一個this參數。 */class Base {public:virtual void f() { cout << "Base::f()" << endl; }virtual void g() { cout << "Base::g()" << endl; }virtual void h() { cout << "Base::h()" << endl; }virtual void foo(Base *pThis) { pThis->hello(this); }virtual void hello(Base *pThis) { pThis->h();}//virtual void __stdcall hello(Base *pThis) {} //成員函數指定了__stdcall呼叫慣例};int test(){typedef void(*Fun)();Base *b = new Base;cout << *(int*)(&b) << endl; //虛函數表的地址存放在對象最開始的位置Fun funf = (Fun)(*(int*)*(int*)b);Fun fung = (Fun)(*((int*)*(int*)b + 1));Fun funh = (Fun)(*((int*)*(int*)b + 2));/************************************************************************//* 調用內部無this參與的成員(包括變數和方法)的對象方法 *//************************************************************************///如果下面三個方法裡,沒有用到與對象相關的成員可以不用為ecx賦值,否則會出錯funf();fung();funh();/************************************************************************//* 調用內部有this參與的成員(包括變數和方法)的對象方法 *//************************************************************************///少了__stdcall(注意位置),棧會不平衡了:本來c++預設是thiscall,如果不要,vs編譯器會讓調用者平衡棧,即多了一句 add esp, 4typedef void(__stdcall *Fun_Base)(Base*);Fun_Base foo = Fun_Base(*((int*)*(int*)b + 3));//就是多增加這句,因為編譯器對c++預設採用thiscall_asm{mov ecx, dword ptr[b]}foo(b);//裡面用到this了,不給ecx賦值,this就不對/************************************************************************//* 調用內部有this參與的成員(包括變數和方法)的對象方法,純彙編版本 *//************************************************************************///嘗試調用虛函數表的第四個方法_asm{//同上,如果所call的方法裡,沒有用到與對象相關的成員可以不用為ecx賦值,否則會出錯mov ecx, dword ptr[b]push ecx //一個入參//mov ecx, b或mov eax, [b],表達同一個意思,vs最後都是mov ecx, dword ptr[b];//實質是mov eax, [EBP-04h],結果表現為ecx=b,即從棧上擷取指標b的值,而不是*b(即*(int*)b == ptr_vftable)的值。mov eax, [b] //擷取對象指標mov eax, [eax] //虛函數表首地址,即對象的開始處,ptr_vftable = [eax + 0] = [ecx] = [this_of_b] = [b]call [eax + 0x0c] //調用虛函數表的第四個,某虛函數表裡第N個方法(x86),[ptr_vftable + (N - 1) * 4] = [[eax]+(N-1)*4]}/************************************************************************//* 正常的調用 *//************************************************************************//*00EF9886 8B 45 F4 mov eax, dword ptr[b]00EF9889 50 push eax00EF988A 8B 4D F4 mov ecx, dword ptr[b]00EF988D 8B 11 mov edx, dword ptr[ecx]00EF988F 8B 4D F4 mov ecx, dword ptr[b]00EF9892 8B 42 0C mov eax, dword ptr[edx + 0Ch]00EF9895 FF D0 call eax*/b->foo(b);//http://blog.csdn.net/haoel/article/details/1948051//*虛函數表的結束結點,就像字串的結束符“/0”一樣,其標誌了虛函數表的結束。這個結束標誌的值在不同的編譯器下是不同的。1. 在WinXP+VS2003下,這個值是NULL。2. 而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,2.1 這個值是如果1,表示還有下一個虛函數表,2.2 如果值是0,表示是最後一個虛函數表。*/cout << (Fun)(*((int*)*(int*)b + 5)); // 最後一個位置為0,表明虛函數表的結束return 0;}
C++虛函數介紹
1. 先從C++看看下虛函數表和多態表現 http://blog.csdn.net/haoel/article/details/1948051/
2. 有虛函數的類的大小 http://blog.csdn.net/hackbuteer1/article/details/7883531
3. 再從彙編看,類的繼承,虛函數表的形成,表放在哪個地址等 http://www.pediy.com/kssd/pediy10/60538.html
虛函數表的形成,是在類建構函式裡,對執行個體對象首地址裡存放的虛函數數組進行修改實現的。
class Child :public Base{public:virtual void vf1(){cout<<"I‘m in sub Class.";}virtual void vf2(){cout<<"I‘m in sub Class.";}//...}
.text .rdata?class Child記憶體 這個類的所有對象所共有的+-------------+ +-----+-----+-----+-----+----+| ptr_vftable | -------> | vf1 | vf2 | vf3 | ... |end?|+-------------+ +-----+-----+-----+-----+----+| ||other members|| |+-------------+
逆向C++(中文版)
http://wenku.baidu.com/link?url=bjLVj2eqfe29_Edzi99MBGJeoCtVaHDXj-3r4s4lm771BAQnJ0WIUaQywPZgGq3Yz_uU9yh-B0V6q5SFMUhRo0t436BUnUdaHuhpwERvLvC
C++多態性:
GoF著作中未提到的設計模式(4):Double Dispatch
http://www.cnblogs.com/west-link/archive/2011/07/26/2116887.html
http://en.wikipedia.org/wiki/Double_dispatch
C++成員不通過對象調用的直接調用寫法