在C ++中处理层次结构内成员函数的指针

时间:2010-05-26 19:54:12

标签: c++ inheritance member-function-pointers

我正在尝试编写以下情况: 我有一个基类提供处理事件的框架。我正在尝试使用指针到成员函数的数组。它如下:

class EH { // EventHandler
   virtual void something(); // just to make sure we get RTTI
public:
  typedef void (EH::*func_t)();
protected:
  func_t funcs_d[10];
protected:
  void register_handler(int event_num, func_t f) {
    funcs_d[event_num] = f;
  }
public:
  void handle_event(int event_num) {
    (this->*(funcs_d[event_num]))();
  }
};

然后,用户应该从这个类派生其他类并提供处理程序:

class DEH : public EH {
public:
  typedef void (DEH::*func_t)();
  void handle_event_5();
  DEH() {
     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile
     ........
  }
};

此代码无法编译,因为DEH :: func_t无法转换为EH :: func_t。这对我来说非常有意义。在我的情况下,转换是安全的,因为this下的对象实际上是DEH。所以我想有类似的东西:

void EH::DEH_handle_event_5_wrapper() {
  DEH *p = dynamic_cast<DEH *>(this);
  assert(p != NULL);
  p->handle_event_5();
}

然后代替

     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile
DEH :: DEH()中的

     register_handler(5, &EH::DEH_handle_event_5_wrapper); 

所以,最后问题(花了我足够长的时间......): 有没有办法自动创建这些包装器(如EH::DEH_handle_event_5_wrapper)? 或者做类似的事情? 那种情况有哪些其他解决方案呢?

感谢。

3 个答案:

答案 0 :(得分:2)

您可以使用static_castDEH::func_t转换为EH::func_t,而不是为所有派生类中的每个处理程序创建包装器(当然,甚至远程都不是可行的方法)。成员指针是逆变:它们在层次结构中自然转换,可以使用static_cast在层次结构中手动转换它们(与普通对象指针相反,它们是协变 )。

您正在处理的情况正是static_cast功能被扩展为允许成员指针upcast的原因。此外,成员函数指针的非平凡内部结构也是专门为正确处理这种情况而实现的。

所以,你可以简单地做

DEH() {
   func_t f5 = &DEH::handle_event_5;
   register_handler(5, static_cast<EH::func_t>(f5));
   ........
}

我想说在这种情况下定义一个typedef名称DEH::func_t是没有意义的 - 它是没用的。如果删除DEH::func_t的定义,典型的注册码将如下所示

DEH() {
   func_t f5 = static_cast<func_t>(&DEH::handle_event_5); 
   // ... where `func_t` is the inherited `EH::func_t`
   register_handler(5, f5);
   ........
}

为了让它看起来更优雅,您可以在register_handler中为DEH提供包装,或者使用其他方法(宏?模板?)来隐藏演员。

此方法不提供任何方法来验证调用时处理程序指针的有效性(就像在基于包装器的版本中使用dynamic_cast一样)。我不知道你有多少关心这个检查到位。我会说,在这种情况下,它实际上是不必要的和过度的。

答案 1 :(得分:0)

你真的不应该这样做。查看boost :: bind

http://www.boost.org/doc/libs/1_43_0/libs/bind/bind.html

<强>精化

首先,我建议你重新考虑你的设计。我见过的大多数事件处理程序系统都涉及一个外部注册器对象,它维护事件到处理程序对象的映射。您已将注册嵌入到EventHandler类中,并且正在基于函数指针进行映射,这是不太理想的。您遇到了问题,因为您正在绕过内置的虚函数行为。

boost::bind之类的要点是用函数指针创建对象,允许您利用面向对象的语言特性。因此,基于boost::bind并以您的设计为出发点的实现看起来像这样:

struct EventCallback
{
    virtual ~EventCallback() { }
    virtual void handleEvent() = 0;
};

template <class FuncObj>
struct EventCallbackFuncObj : public IEventCallback
{
    EventCallbackT(FuncObj funcObj) :
        m_funcObj(funcObj) { }
    virtual ~EventCallbackT() { }

    virtual void handleEvent()
    {
        m_funcObj();
    }

    private:
        FuncObj m_funcObj;
};

然后你的register_handler函数看起来像这样:

  void register_handler(int event_num, EventCallback* pCallback) 
  {
      m_callbacks[event_num] = pCallback;
  }

您的注册电话会像:

register_handler(event, 
    new EventCallbackFuncObj(boost::bind(&DEH::DEH_handle_event_5_wrapper, this)));

现在,您可以从任何类型的(对象,成员函数)创建一个回调对象,并将其保存为给定事件的事件处理程序,而无需编写自定义函数包装器对象。

答案 2 :(得分:0)

为什么不使用虚拟功能?像

这样的东西
class EH {
public:
  void handle_event(int event_num) {

    // Do any pre-processing...

    // Invoke subclass hook
    subclass_handle_event( event_num );

    // Do any post-processing...
  }
private:
  virtual void subclass_handle_event( int event_num ) {}
};

class DEH : public EH {
public:
  DEH() { }
private:
  virtual void subclass_handle_event( int event_num ) {
     if ( event_num == 5 ) {
        // ...
     }
  }
};