C++成员不通过对象调用的直接调用写法
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, 4 typedef 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 eax 00EF988A 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