提问人:fryguybob 提问时间:9/30/2008 最后编辑:fryguybob 更新时间:1/29/2016 访问量:32736
在特定对象实例上调用 C++ 函数指针
Calling a C++ function pointer on a specific object instance
问:
我有一个函数指针,定义如下:
typedef void (*EventFunction)(int nEvent);
有没有办法使用 C++ 对象的特定实例处理该函数?
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne() { handler(1); }
};
class B
{
private:
A a;
public:
B() { a.SetEvent(EventFromA); } // What do I do here?
void EventFromA(int nEvent) { // do stuff }
};
编辑:Orion 指出了 Boost 提供的选项,例如:
boost::function<int (int)> f;
X x;
f = std::bind1st(
std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)
不幸的是,Boost 对我来说不是一个选择。是否有某种可以用 C++ 编写的“咖喱”函数,将指向成员函数的指针包装到普通函数指针中?
答:
遗憾的是,EventFunction 类型不能指向 B 的函数,因为它不是正确的类型。你可以把它设成正确的类型,但这可能不是你真正想要的解决方案:
typedef void (*B::EventFunction)(int nEvent);
...然后,一旦您用 B 的 obhect 调用回调,一切正常。但是您可能希望能够在 B 之外调用函数,在执行其他操作的其他类中调用函数。这就是回调的意义所在。但现在这种类型肯定指向 B 中的某些东西。更具吸引力的解决方案是:
- 将 B 设为基类,然后为可能调用的其他类重写一个虚函数。然后,A 存储指向 B 的指针,而不是函数指针。更干净。
- 如果你不想将函数绑定到特定的类类型,甚至是基类(我不会责怪你),那么我建议你将被称为静态函数的函数命名为:“”。然后你可以直接调用它,而不需要 B 的对象。但您可能希望它调用 B 的特定实例(除非 B 是单例)。
static void EventFrom A(int nEvent);
- 因此,如果您希望能够调用 B 的特定实例,但也能够调用非 B 的实例,那么您需要将其他内容传递给回调函数,以便回调函数可以调用正确的对象。如上所述,使函数为静态函数,并添加一个 void* 参数,该参数将指向 B。
在实践中,您会看到这个问题的两种解决方案:传递 void* 和事件的临时系统,以及在基类中具有虚函数的层次结构,如窗口系统
您可能会发现马歇尔·克莱恩 (Marshall Cline) 的 C++ 常见问题解答对您要完成的任务很有帮助。
阅读有关指向成员的指针的信息。 若要在派生类上调用方法,必须在基类中将该方法声明为虚拟方法,并在基类中重写,并且指针应指向基类方法。有关指向虚拟成员的指针的详细信息。
目前还不清楚你在这里要完成什么。显而易见的是,函数指针不是方法。
也许你要找的是指向方法的指针。
评论
远离原始 C++ 函数指针,改用 std::function
。
如果您使用的是不支持 C++ 11 的旧编译器(如 Visual Studio 2008),则可以使用 boost::function
。
并且是一回事 - 他们将相当多的提升内容拉入 C++ 11 的 std 库中。boost:function
std::function
注意:您可能需要阅读 boost 函数文档而不是 microsoft 文档,因为它更容易理解
评论
您可以使用函数指针索引到给定对象实例的 vtable 中。这称为成员函数指针。您的语法需要更改以使用 “.*” 和 “&::” 运算符:
class A;
class B;
typedef void (B::*EventFunction)(int nEvent)
然后:
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};
class B
{
private:
A a;
public:
B() { a.SetEvent(&B::EventFromA); } // note: "&::"
void EventFromA(int nEvent) { /* do stuff */ }
};
我有一组类,用于我在 c++ 框架中使用的确切内容。
http://code.google.com/p/kgui/source/browse/trunk/kgui.h
我的处理方式是,每个可以用作回调的类函数都需要一个静态函数来绑定对象类型。我有一组可以自动执行此操作的宏。它创建一个具有相同名称的静态函数,除了带有“CB_”前缀和一个额外的第一个参数,即类对象指针。
查看类类型 kGUICallBack 及其各种模板版本,以处理不同的参数组合。
#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
如果要与 C 库交互,则如果不使用类似 .大多数采用回调函数的 C 库通常还允许您传递您选择的额外参数(通常是 类型 ),您可以使用该参数来引导您的类,如下所示:boost::bind
void*
class C
{
public:
int Method1(void) { return 3; }
int Method2(void) { return x; }
int x;
};
// This structure will hold a thunk to
struct CCallback
{
C *obj; // Instance to callback on
int (C::*callback)(void); // Class callback method, taking no arguments and returning int
};
int CBootstrapper(CCallback *pThunk)
{
// Call the thunk
return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}
void DoIt(C *obj, int (C::*callback)(void))
{
// foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
struct CCallback thunk = {obj, callback};
foobar(&CBootstrapper, &thunk);
}
int main(void)
{
C c;
DoIt(&c, &C::Method1); // Essentially calls foobar() with a callback of C::Method1 on c
DoIt(&c, &C::Method2); // Ditto for C::Method2
}
我强烈推荐 Don Clugston 出色的 FastDelegate 库。它提供了您对真实委托的所有期望,并在大多数情况下编译为一些 ASM 指令。随附的文章也是对成员函数指针的良好阅读。
http://www.codeproject.com/KB/cpp/FastDelegate.aspx
评论
std::function
你提到提升对你来说不是一个选择,但你有TR1吗?
TR1 提供基于 boost 库的函数、绑定和mem_fn对象,您可能已经将其与编译器捆绑在一起。它还没有标准,但我最近使用的至少两个编译器已经拥有它。
http://en.wikipedia.org/wiki/Technical_Report_1 http://msdn.microsoft.com/en-us/library/bb982702.aspx
评论