【回调函数】 如何将类的成员函数作为回调函数?

-小仙- 2011-02-21 04:21:55
加精
我目前可以 通过将类的成员函数通过转换的方式实现回调,
但是因为类的成员函数的指针被转换为其它的指针,
所以

如果,无法在该 函数中调用 本类的其它成员 ?



不知道由没有其它的实现转换的方式,



类似于MFC中,按钮的消息中 类的成员函数 的注册一样!!
...全文
11398 196 打赏 收藏 举报
写回复
196 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
wjlsmail 2013-04-26
如果回调是自己发起,怎么做都可以,如果不是,用类的static函数就有问题了
  • 打赏
  • 举报
回复
zhangyanwei12 2011-03-18
任何成员函数都可以变成回调函数,不仅仅是静态成员函数,但需要一些特殊的处理,看上面的例子就知道了;
  • 打赏
  • 举报
回复
zhangyanwei12 2011-03-18
#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
#include "assert.h"
#include "stdafx.h"
//////////////////////////////////////////////////////////////////////////
// 回调函数类型定义
typedef int (CALLBACK pfaCallBack)(int, long, char);
//////////////////////////////////////////////////////////////////////////
// thunk 结构定义
// 由于thunk 要被当作代码来执行,因此thunk 结构必须是字节对齐的,这里使用
// VC++ 的修饰符号#pragma pack(push, 1) 来定义一个字节对齐的结构体
// 之后通过#pragma(pop) 恢复默认对齐模式
#pragma pack(push, 1)
struct Thunk
{
BYTE op_movecx;
DWORD_PTR val_ecx;
BYTE op_call;
DWORD_PTR val_address;
};
#pragma pack(pop)
//////////////////////////////////////////////////////////////////////////
// 一个类的定义,就这样平静的开始了
class Dummy {
// 一个成员变量
private:
int m_id ;
// 定义一个thunk
private:
Thunk m_thunk;
// 定义构造函数,在构造函数中设置m_id值
public:
Dummy(int id):m_id(id)
{
}
//////////////////////////////////////////////////////////////////////////
// 定义一个回调函数,另外他还是个类的成员函数呢
public:
int memberCallback(int intVal, long longVal, char charVal)
{
// 做自己想做的事情
printf("\nI am a member function of class Dummy""(Dummy::memberCallback),ID = %d.""\nI got the value 0x%08x 0x%08x \'%c\'"
, m_id, intVal, longVal, charVal);
return m_id;
}
//////////////////////////////////////////////////////////////////////////
// 初始化thunk 的数据,这里是关键
public:
void InitThunk()
{
// 0xB9是‘mov ecx, 数值’的机器码,xB9之后的个字节(32位)指定了要
// 给ecx的数值.
m_thunk.op_movecx = 0xB9;
// 填写要给ecx的数值为this(类对象的指针)
m_thunk.val_ecx = (DWORD_PTR)this;
// 0xE9是‘jmp 相对地址’的机器码。相对地址由xE9之后的个字节(32位)
// 给出。
m_thunk.op_call = 0xE9;
// 获得Dummy::memberCallback的具体地址。关于成员函数与类对象的关系
// 请参阅Stan Lippman 的<<Inside C++ Object Model>>
// 用汇编获得地址省去了用C++带来的难看的语法
DWORD_PTR off = 0
int (Dummy::*temp)(int, long, char);
temp=Dummy::memberCallback;
_asm
{
mov eax, temp
mov DWORD PTR [off], eax
}
// jmp后面是相对地址,因此要求出这个地址
// 相对地址=成员函数地址-跳转点下一指令地址
// 正负号不要紧,jmp自己能根据正负判断如何跳。
m_thunk.val_address =
off - ( (DWORD_PTR)(&m_thunk.val_address) + sizeof(DWORD_PTR) );
}
//////////////////////////////////////////////////////////////////////////
// 返回thunk的地址给要回调他的函数。
// 那个函数还以为thunk是一个函数地址呢。根本不知道thunk是我们自己构造的
// 数据
public:
pfaCallBack GetStaticEntry()
{
return (pfaCallBack)&m_thunk;
}
};
//////////////////////////////////////////////////////////////////////////
// 一个调用回调函数的函数
void Trigger(pfaCallBack callback)
{
assert(callback);
int intVal = 0x1234;
int longVal = 0x5678ABCD;
int charVal = 'D';
// 函数内部
int r;
// 开始回调
r = callback(intVal, longVal, charVal);
printf("\n Return value = %d\n", r);
}
//////////////////////////////////////////////////////////////////////////
// 传说中的主函数。VC++工程里生成的就叫_tmain不叫main。
int _tmain(int argc, _TCHAR argv[])
{
//生成一个对象
Dummy *dummy1 = new Dummy(9);
//初始化thunk
dummy1->InitThunk();
//取得thunk地址
pfaCallBack pCallback1 = dummy1->GetStaticEntry();
//给需要回调函数的函数传递thunk
Trigger(pCallback1);
// 按任意键继续...
system("pause");
return 0;
}
  • 打赏
  • 举报
回复
abcdwell 2011-03-16
  • 打赏
  • 举报
回复
ominging 2011-03-14
[Quote=引用 94 楼 hankanling123 的回复:]
static成员函数就能做为回调函数。
[/Quote]
很好很强大
  • 打赏
  • 举报
回复
wjlsmail 2011-03-13
The static member function can be used as callback function.
  • 打赏
  • 举报
回复
coolria 2011-03-11

static void* CallBack::FUN(void* data){
Proc* proc=(Proc*)data;
return data->operate(data->para);


}
  • 打赏
  • 举报
回复
CppFile 2011-03-09
我明白楼主的意思,楼主是想做到回调函数中能访问类中的成员,就像类的成员函数一样

这个需求等价于“C++中事件的实现”

有很多解决方法,fastdelagate,boost::bind,signal

  • 打赏
  • 举报
回复
mjg711009 2011-03-09
我来了,看看这个东西适合我吗?
  • 打赏
  • 举报
回复
Utopiaaaa 2011-03-09
上面 #183楼 #184楼 的都不用看了 可恶没权限删不掉 修改如下:

#include <windows.h>
#include <iostream>
using namespace std;

class CAClass
{
public:
CAClass();
typedef void(CALLBACK *fnShowCallBack)();//函数指针
static void CALLBACK show(); //回调函数
void memShow(int); //成员函数

public:
fnShowCallBack m_fnCallback; //函数指针 用于实现回调
int m_imem;

private:
static CAClass* pToThis; //静态全局指针
void initpToThis(); //使全局指针pToThis指向this
void callbackShow(); //回调专用函数
};

//静态全局指针初始化为NULL
CAClass* CAClass::pToThis = NULL;

CAClass::CAClass():
m_imem(100)
{
//使全局指针pToThis指向this
CAClass::initpToThis();
}

//使全局指针pToThis指向this
void CAClass::initpToThis()
{
pToThis = this;
}

//回调函数
void CAClass::show()
{
//pToThis->memShow(456); //用全局指针实现调用
return pToThis->callbackShow();//返回一成员函数(回调专用)以避免使用pToThis
}

//回调专用
void CAClass::callbackShow()
{
cout<<"回调函数的操作转到这个函数中 免去全局指针的使用"<<endl;
pToThis->memShow(123); //可以用全局指针
memShow(123); //也可以免去全局指针
}

//成员函数
void CAClass::memShow(int value)
{
cout<<"输出成员变量:"<<m_imem<<endl;
cout<<"成员函数调用:"<<value<<endl;
}

int main()
{
//第一种回调方法(比较怪异,暗藏危机。只是说明回调的用法)
//如果没有实例化对象这种方法会失败(不实例化对象可以寻址“成员函数”却无法寻址“成员变量”)
//具体原因可以参考《深度探索c++对象模型》一书,我不能提供比较理想的解释

CAClass ob1;//没有这一句这种方法会失败,如果去掉“成员变量”m_imem就会成功
ob1.m_imem = 111111;
cout<<"第一种回调方法:"<<endl;
CAClass::fnShowCallBack pFn = NULL;//定义函数指针
pFn = CAClass::show;
if (pFn!=NULL)
{
pFn();//此处实现回调
}

//第二种回调方法
cout<<"\n"<<"第二种回调方法:"<<endl;
CAClass ob;
ob.memShow(123);//对象调用
ob.m_fnCallback = CAClass::show;
if (ob.m_fnCallback!=NULL)
{
ob.m_fnCallback();//此处实现回调
}

cout<<endl;

//注意此处pFn()与第一次调用pFn()的结果的区别
//具体原因可以参考《深度探索c++对象模型》一书,我不能提供比较理想的解释
pFn();

cout<<endl;

//其他方法实现回调.............
return 1;
}

  • 打赏
  • 举报
回复
Utopiaaaa 2011-03-09
#183楼补充:第一种调用:如果被调用类成员函数中涉及任何“成员变量”的操作一定会失败(不定义对象可以寻址成员函数却无法寻址成员变量)
下面代码:
//未实例化类对象,因为上面代码被调用的成员“函数”中不涉及对成员“变量”的操作所以会成功执行;
cout<<"一种回调方法:"<<endl;
CAClass::fnShowCallBack pFn = NULL;//定义函数指针
pFn = CAClass::show;
if (pFn!=NULL)//此处判断多余 习惯问题
{
pFn();//此处响应回调函数
}
除非在这段代码前面定义对象:例如加上CAClass ob1;

cout<<"一种回调方法:"<<endl;
CAClass ob1;//加上这一句就不会出错了
CAClass::fnShowCallBack pFn = NULL;//定义函数指针
pFn = CAClass::show;
if (pFn!=NULL)//此处判断多余 习惯问题
{
pFn();//此处响应回调函数
}


比较怪异的用法,具体原因可以参考《深度探索c++对象模型》一书,我不能提供比较理想的解释。


  • 打赏
  • 举报
回复
Utopiaaaa 2011-03-09
下面是我用的方法 鄙人愚见哈 只是希望能提供一点思路 疏漏之处难免 不做无误担保
#include <windows.h>
#include <iostream>
using namespace std;

class CAClass
{
public:
CAClass();
typedef void(CALLBACK *fnShowCallBack)();//函数指针
public:
static void CALLBACK show(); //回调函数
void callbackShow(); //回调专用函数
void memShow(int); //成员函数
void initpToThis(); //使全局指针pToThis指向this
static CAClass* pToThis; //静态全局指针
fnShowCallBack m_fnCallback; //函数指针 用于实现回调
};

//静态全局指针初始化为NULL
CAClass* CAClass::pToThis = NULL;

CAClass::CAClass()
{
//使全局指针pToThis指向this
CAClass::initpToThis();
}

//使全局指针pToThis指向this
void CAClass::initpToThis()
{
pToThis = this;
}

//回调函数
void CAClass::show()
{
//pToThis->memShow(456); //用全局指针实现调用
return pToThis->callbackShow();//返回一成员函数(回调专用)以避免使用pToThis
}

//回调专用
void CAClass::callbackShow()
{
cout<<"回调函数的操作转到这个函数中 免去全局指针的使用"<<endl;
pToThis->memShow(123); //可以用全局指针
memShow(123); //也可以免去全局指针
}

//成员函数
void CAClass::memShow(int value)
{
cout<<"成员函数调用:"<<value<<endl;
}

int main()
{
cout<<"一种回调方法:"<<endl;
CAClass::fnShowCallBack pFn = NULL;//定义函数指针
pFn = CAClass::show;
if (pFn!=NULL)//此处判断多余 习惯问题
{
pFn();//此处响应回调函数
}

cout<<"\n"<<"另一种回调方法:"<<endl;
CAClass ob;
ob.memShow(123);//对象调用
ob.m_fnCallback = CAClass::show;
if (ob.m_fnCallback!=NULL)
{
ob.m_fnCallback();
}
cout<<endl;
//...........
return 1;
}
  • 打赏
  • 举报
回复
Roy_Smiling 2011-03-09
static
  • 打赏
  • 举报
回复
pqj82 2011-03-09
谢谢诶楼主。没见过啊
  • 打赏
  • 举报
回复
天乐_那由他 2011-03-09
推荐你参考或使用FastDelegate, codeproject上有

  • 打赏
  • 举报
回复
TianWeiV 2011-03-08
方法真不少
  • 打赏
  • 举报
回复
qman007 2011-03-08
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <process.h>
class clsTest
{
public:
clsTest(){m_intVal=0;};
int m_intVal;
public:
UINT __stdcall myFunc()
{
char sBuf[20];
while(1)
{
Sleep(1000);
itoa(m_intVal++,sBuf,10);
MessageBox(0,sBuf,0,0);
}
}
};
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

typedef UINT (WINAPI clsTest::*pMyFunc)();
typedef UINT (WINAPI *pFunc)(void *);
clsTest *pObj = new clsTest();
pMyFunc func = pObj->myFunc;
pFunc func2;
memcpy(&func2,&func,sizeof(func2));
UINT id;
HANDLE hThread = (HANDLE)::_beginthreadex(0,0,func2,(void *)pObj,0,&id);
Sleep(1000000);
return 0;
}


这个就是楼主需要的,关键是this 指针通过 __beginthreadex的参数就能传入。
大家不用怀疑了,完全好用。
  • 打赏
  • 举报
回复
CppFile 2011-03-08
c++Builder就是这么实现的,真好用啊,可惜bcb式微了啊!
  • 打赏
  • 举报
回复
tyuiyang 2011-03-07
不错Static肯定构不成回调,这想都不用想.
  • 打赏
  • 举报
回复
kd_xsy 2011-03-07
哎,差距怎么这么大啊···看来我这个新手要加紧学习啊!
  • 打赏
  • 举报
回复
加载更多回复(112)
相关推荐
发帖
VC/MFC

1.6w+

社区成员

VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
帖子事件
创建了帖子
2011-02-21 04:21
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……