【问题标题】:Calling a C++ function pointer on a specific object instance在特定对象实例上调用 C++ 函数指针
【发布时间】:2010-09-14 03:57:20
【问题描述】:

我有一个函数指针定义为:

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++ 编写的“currying”函数,它将指向成员函数的指针包装到普通函数指针中?

【问题讨论】:

  • 是的,有一个 C++ 柯里化函数,称为 tr1::bind 或 boost::bind!是的,有一个用于保存咖喱结果的包装器。它被称为 tr1::function 或 boost::function。
  • 有人可以使用 C++11 std::function 提供代码 sn-p 答案吗?我正在学习 C++,这让我感到困惑,因为我希望有这样一个基本的东西可用,但以下所有恢复为 boost 或 c++11 的答案实际上并没有提供代码 sn-p / howto .

标签: c++ pointers function


【解决方案1】:

不幸的是,EventFunction 类型不能指向 B 的函数,因为它不是正确的类型。您可以将其设为正确的类型,但这可能不是您真正想要的解决方案:

typedef void (*B::EventFunction)(int nEvent);

... 然后,一旦您使用 B 的对象调用回调,一切都会正常工作。但是您可能希望能够在 B 之外的其他类中调用函数来做其他事情。这就是回调的重点。但现在这种类型肯定指向 B 中的某些东西。更有吸引力的解决方案是:

  • 将 B 设为基类,然后为每个可能被调用的其他类覆盖一个虚函数。然后 A 存储指向 B 的指针而不是函数指针。干净多了。
  • 如果您不想将函数绑定到特定的类类型,甚至是基类(我不会责怪您),那么我建议您将被称为静态函数的函数:“@987654322 @"。然后您可以直接调用它,而不需要 B 的对象。但您可能希望它调用 B 的特定实例(除非 B 是单例)。
  • 因此,如果您希望能够调用 B 的特定实例,但也能够调用非 B 的实例,那么您需要将其他内容传递给您的回调函数,以便回调函数可以调用正确的对象.如上所述,使您的函数成为静态函数,并添加一个 void* 参数,您将创建一个指向 B 的指针。

在实践中,您会看到此问题的两种解决方案:传递 void* 和事件的临时系统,以及在基类中具有虚函数的层次结构,例如窗口系统

【讨论】:

    【解决方案2】:

    您可能会发现 C++ FAQ by Marshall Cline 对您想要完成的工作很有帮助。

    【讨论】:

      【解决方案3】:

      了解pointers to members。 要在派生类上调用方法,该方法必须在基类中声明为虚拟方法并在基类中覆盖,并且您的指针应指向基类方法。更多关于pointers to virtual members

      【讨论】:

        【解决方案4】:

        有点不清楚您要在这里完成什么。很明显,函数指针不是办法。

        也许你正在寻找的是指向方法的指针。

        【讨论】:

        • 第二点。如果您提供更多信息,我们可以提供更多帮助。
        【解决方案5】:

        远离原始 C++ 函数指针,改用 std::function

        如果您使用的是不支持 C++11 的旧编译器(例如 Visual Studio 2008),则可以使用 boost::function
        boost:functionstd::function 是一回事 - 他们拉了很多在 C++11 的标准库中添加了一些提升内容。

        注意:您可能需要阅读 boost 函数文档而不是 microsoft 文档,因为它更容易理解

        【讨论】:

        • 或者 tr1::function 和 tr1::bind。
        • ...这是同一件事:)
        【解决方案6】:

        您可以使用函数指针来索引给定对象实例的 vtable。这称为member function pointer。您的语法需要更改以使用“.*”和“&::”运算符:

        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 */ }
        };
        

        【讨论】:

          【解决方案7】:

          我有一组类用于我在我的 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);}
          

          【讨论】:

            【解决方案8】:

            如果您正在与 C 库进行交互,那么如果不使用 boost::bind 之类的东西,就无法使用类成员函数。大多数采用回调函数的 C 库通常还允许您传递您选择的额外参数(通常是 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
            }
            

            【讨论】:

              【解决方案9】:

              我强烈推荐 Don Clugston 出色的 FastDelegate 库。它提供了您对真正委托的所有期望,并在大多数情况下编译为一些 ASM 指令。随附的文章也是有关成员函数指针的好读物。

              http://www.codeproject.com/KB/cpp/FastDelegate.aspx

              【讨论】:

              • 我应该注意,如果可能的话,如果支持 C++11,你应该使用std::function。 FastDelegate 已经有一段时间没有更新了,可能不会永远工作。
              【解决方案10】:

              你提到提升不是你的选择,但你有 TR1 可用吗?

              TR1 提供基于 boost 库的函数、绑定和 mem_fn 对象,您可能已经将它与编译器捆绑在一起。它还不是标准的,但我最近使用的至少两个编译器都有它。

              http://en.wikipedia.org/wiki/Technical_Report_1
              http://msdn.microsoft.com/en-us/library/bb982702.aspx

              【讨论】:

              • 很好的建议,但我的具体项目是旧版 VC6 代码。这也是我无法通过更好的 C++ 设计选择真正解决问题的原因sigh
              猜你喜欢
              • 1970-01-01
              • 2019-10-19
              • 1970-01-01
              • 1970-01-01
              • 2012-04-26
              • 1970-01-01
              • 2013-07-19
              • 1970-01-01
              • 2012-10-12
              相关资源
              最近更新 更多