在C++中,成员函数的指针是个比较特殊的东西。对普通的函数指针来说,可以视为一个地址,在需要的时候可以任意转换并直接调用。但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法。C++专门为成员指针准备了三个运算符: "::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数。
// Thunk.cpp : Defines the entry point for the console application.
// #include "stdafx.h" typedef unsigned int DWORD; //取类成员函数的地址.vc8版本.可以取私有成员函数地址.
#define GetMemberFuncAddr_VC8(FuncAddr,FuncType)\
{ \
__asm \
{ \
mov eax,offset FuncType \
}; \
__asm \
{ \
mov FuncAddr, eax \
}; \
} //取类成员函数的地址.vc6版本.
template <class ToType, class FromType>
void GetMemberFuncAddr_VC6(ToType& addr,FromType f)
{
union
{
FromType _f;
ToType _t;
}ut; ut._f = f; addr = ut._t;
} //调用类成员函数
DWORD CallMemberFunc(int callflag,DWORD funcaddr,void *This,int count,...)
{
DWORD re; if(count>)//有参数,将参数压入栈.
{
__asm
{
mov ecx,count;//参数个数,ecx,循环计数器. mov edx,ecx;
shl edx,;
add edx,0x14; edx = count*+0x14; next: push dword ptr[ebp+edx];
sub edx,0x4;
dec ecx
jnz next;
}
} //处理this指针.
if(callflag==) //__thiscall,vc默认的成员函数调用类型.
{
__asm mov ecx,This;
}
else//__stdcall
{
__asm push This;
} __asm//调用函数
{
call funcaddr; //call 1.向堆栈中压入下一行程序的地址;2.JMP到call的子程序地址处。
mov re,eax;
} return re;
} //////////////////////////////////////////////////////////////////////////////////////////////////
void test1()//演示c++成员函数指针的用法.
{
class tt
{
public: void foo(int x){ printf("\n %d \n",x); }
}; typedef void (tt::* FUNCTYPE)(int); FUNCTYPE ptr = &tt::foo; //给一个成员函数指针赋值. tt a;
(a.*ptr)(); //调用成员函数指针. tt *b = new tt;
(b->*ptr)(); //调用成员函数指针. delete b;
// DWORD dwFooAddrPtr= 0;
// dwFooAddrPtr = (DWORD) &tt::foo; /* Error C2440 */
// dwFooAddrPtr = reinterpret_cast<DWORD> (&tt::foo); /* Error C2440 */
} void test2()//示范如何取成员函数地址.
{
class tt
{
public: void foo(int x){ printf("\n %d \n",x); }
}; #if _MSC_VER >1200
DWORD dwAddrPtr1;
GetMemberFuncAddr_VC8(dwAddrPtr1,tt::foo);
printf("\n test2 tt::foo %08x",dwAddrPtr1);
#endif DWORD dwAddrPtr2;
GetMemberFuncAddr_VC6(dwAddrPtr2,&tt::foo);
printf("\n test2 tt::foo %08x",dwAddrPtr2);
} void test3()//示范如何调用成员函数地址.
{
class tt
{
public: void foo(int x,char c,char *s)//没有指定类型,默认是__thiscall.
{
printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
} void __stdcall foo2(int x,char c,char *s)//成员函数指定了__stdcall调用约定.
{
printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
} int m_a;
}; typedef void (__stdcall *FUNCTYPE) ( int,char,char*);//定义对应的非成员函数指针类型,注意指定__stdcall.
typedef void (__stdcall *FUNCTYPE2)(void *,int,char,char*);//注意多了一个void *参数. tt abc;
abc.m_a = ; DWORD ptr;
DWORD This = (DWORD)&abc; GetMemberFuncAddr_VC6(ptr,&tt::foo); //取成员函数地址. FUNCTYPE fnFooPtr = (FUNCTYPE) ptr;//将函数地址转化为普通函数的指针. __asm //准备this指针.
{
mov ecx, This;
} fnFooPtr(,'a',"7xyz"); //象普通函数一样调用成员函数的地址. GetMemberFuncAddr_VC6(ptr,&tt::foo2); //取成员函数地址. FUNCTYPE2 fnFooPtr2 = (FUNCTYPE2) ptr;//将函数地址转化为普通函数的指针. fnFooPtr2(&abc,,'a',"7xyz"); //象普通函数一样调用成员函数的地址,注意第一个参数是this指针.
} void test4()//示范通过CallMemberFunc调用成员函数
{
class tt
{
public: void foo(int x,char c,char *s)//没有指定类型,默认是__thiscall.
{
printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
} void __stdcall foo2(int x,char c,char *s)//成员函数指定了__stdcall调用约定.
{
printf("\n m_a=%d, %d,%c,%s\n",m_a,x,c,s);
} int m_a;
}; tt abc;
abc.m_a = ; DWORD ptr1,ptr2; GetMemberFuncAddr_VC6(ptr1,&tt::foo); //取成员函数地址.
GetMemberFuncAddr_VC6(ptr2,&tt::foo2); //取成员函数地址. CallMemberFunc(,ptr1,&abc,,,'a',"7xyz");//第一个参数0,表示采用__thiscall调用.
CallMemberFunc(,ptr2,&abc,,,'a',"7xyz");//第一个参数1,表示采用非__thiscall调用.
} void test5()//示范在继承情况下使用函数地址.
{
class tt1
{
public:
void foo1(){ printf("\n hi, i am in tt1::foo1\n"); }
virtual void foo3(){ printf("\n hi, i am in tt1::foo3\n"); }
}; class tt2 : public tt1
{
public:
void foo2(){ printf("\n hi, i am in tt2::foo2\n"); }
virtual void foo3(){ printf("\n hi, i am in tt2::foo3\n"); }
}; DWORD tt1_foo3,tt2_foo1,tt2_foo2,tt2_foo3; GetMemberFuncAddr_VC6(tt1_foo3,&tt1::foo3);
GetMemberFuncAddr_VC6(tt2_foo1,&tt2::foo1);
GetMemberFuncAddr_VC6(tt2_foo2,&tt2::foo2);
GetMemberFuncAddr_VC6(tt2_foo3,&tt2::foo3); tt1 x;
tt2 y; CallMemberFunc(,tt1_foo3,&x,); // tt1::foo3
CallMemberFunc(,tt2_foo1,&x,); // tt2::foo1 = tt1::foo1
CallMemberFunc(,tt2_foo2,&x,); // tt2::foo2
CallMemberFunc(,tt2_foo3,&x,); // tt2::foo3 CallMemberFunc(,tt1_foo3,&y,); // tt1::foo3
CallMemberFunc(,tt2_foo1,&y,); // tt2::foo1 = tt1::foo1
CallMemberFunc(,tt2_foo2,&y,); // tt2::foo2
CallMemberFunc(,tt2_foo3,&y,); // tt2::foo3
} int main(int argc, char* argv[])
{
test1();
test2();
test3();
test4();
test5(); return ;
}

http://download.csdn.net/download/tikycc2/1580557

直接调用类成员函数地址(用汇编取类成员函数的地址,各VS版本还有所不同)的更多相关文章

  1. 从汇编看c++成员函数指针(三)

    前面的从汇编看c++中成员函数指针(一)和从汇编看c++成员函数指针(二)讨论的要么是单一类,要么是普通的多重继承,没有讨论虚拟继承,下面就来看一看,当引入虚拟继承之后,成员函数指针会有什么变化. 下 ...

  2. 从汇编看c++成员函数指针(二)

    下面先看一段c++源码: #include <cstdio> using namespace std; class X { public: virtual int get1() { ; } ...

  3. .static 和const分别怎么用,类里面static和const可以同时修饰成员函数吗。

    static的作用: 对变量: 1.局部变量: 在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量. 1)内存中的位置:静态存储区 2)初始化:局部的静态变量只能被初始化一次, ...

  4. c++特性:指向类成员的指针和非类型类模板参数和函数指针返回值 参数推导机制和关联型别

    一.c++允许定义指向类成员的指针,包括类函数成员指针和类数据成员指针 格式如下: class A { public: void func(){printf("This is a funct ...

  5. C++ 友元 (全局函数做友元) (类做友元) (成员函数做友元)

    1 //友元 全局函数做友元 2 /* 3 #include <iostream> 4 #include <string> 5 using namespace std; 6 7 ...

  6. C++ 类 & 对象-C++ 内联函数-C++ this 指针-C++ 类的静态成员

    C++ 内联函数 C++ 内联函数是通常与类一起使用.如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方. 对内联函数进行任何修改,都需要重新编译函数的所有客户端 ...

  7. 【转】C++ 虚函数&纯虚函数&抽象类&接口&虚基类

    1. 动态多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 多态性就是允许将子类类型的指针赋值给父类类型 ...

  8. C++ 类的多态三(多态的原理--虚函数指针--子类虚函数指针初始化)

    //多态的原理--虚函数指针--子类虚函数指针初始化 #include<iostream> using namespace std; /* 多态的实现原理(有自己猜想部分) 基础知识: 类 ...

  9. C++ 虚函数&纯虚函数&抽象类&接口&虚基类(转)

    http://www.cnblogs.com/fly1988happy/archive/2012/09/25/2701237.html 1. 多态 在面向对象语言中,接口的多种不同实现方式即为多态.多 ...

随机推荐

  1. 安卓Xpost框架

    http://pcedu.pconline.com.cn/484/4841077_all.html

  2. Using Apache with SSL1(转载)

    SSL/TLS/WTLS原理 作者:yawl < yawl@nsfocus.com >主页:http://www.nsfocus.com日期:2001-02-19 一 前言 首先要澄清一下 ...

  3. phpcms-v9 前台模板文件中{pc}标签的执行流程

    前台pc标签的使用:{pc:content 参数名="参数值" 参数名="参数值" 参数名="参数值"} 如: {pc:content ac ...

  4. [剖析Javascript原理]1.原生数据类型

    一.原生数据类型 JS共有5种原生数据类型: Boolean true或者false String 字符串,在单引号或者双引号之间(不存在字符类型) Number 整数或者浮点数 Null 空 und ...

  5. dropdownlist控件的几个属性selectedIndex、selectedItem、selectedValue、selectedItem.Text、selectedItem.value的区别

    转自http://blog.csdn.net/iqv520/article/details/4419186 1. selectedIndex——指的是dropdownlist中选项的索引,为int,从 ...

  6. ArcEngine栅格和矢量渲染(含可视化颜色带)

    使用ArcEngine9.3开发的栅格和矢量的渲染. 开发环境:ArcEngine9.3,VS2008. 功能:栅格(拉伸和分级)和矢量(简单.唯一值.分级.比例)渲染. 开发界面如图所示. 图1 主 ...

  7. Referrer 还是 Referer?

    上回我写了一篇文章介绍「Referrer Policy」,有小伙伴看完后问我:Referrer 这个单词到底怎么拼,为什么有时候中间有两个 r,有时候只有一个? 是的,这是一个很有趣的问题,这里就给有 ...

  8. CentOS7 vs centos6

    The CentOS Project has announced general availability of CentOS-7, the first release of the free Lin ...

  9. 运行预构建 Linux 映像的 Windows Azure 虚拟机中的交换空间 – 第 1 部分

    本文章由 Azure CAT 团队的 Piyush Ranjan (MSFT) 撰写. 随着基础结构服务(虚拟机和虚拟网络)近期在 Windows Azure 上正式发布,越来越多的企业工作负荷正在向 ...

  10. #include <string>

    1 append(string T&);字符串拼接 2 c_str string.c_str是Borland封装的String类中的一个函数,它返回当前字符串的首字符地址. 3 empty() ...