当类类型未知时,函数指向非静态成员函数?

时间:2012-07-08 10:56:17

标签: c++ inheritance callback function-pointers

我正在开发一个游戏项目,该项目具有在opengl上下文中呈现的临时构建控件;按钮,滚动条,列表框等等。嵌套许多这些控件;例如,我的列表框有一个滚动条,滚动条有3个按钮等。

当滚动条改变值时,我希望它调用响应更改的'some'函数(通常在它的父对象中)。例如,如果列表框有一个滑块,它应该实例化滑块,然后告诉新滑块它应该调用列表框'onScroll(float)'函数。所有控件都共享一个公共基类,所以我可以有一个'base * parent'父指针,然后执行'parent-> onScroll(val)'。但问题是,当父母不从基础中复活时会发生什么;没有虚拟的onScroll()可以跟进,因此顶级父级必须定期检查是否有任何子控件更改了值。这也会混淆其他控件,因为它们甚至可能没有子节点,或者可能需要不同的事件类型,例如选择列表条目对象时等。

更好的解决方案是让子对象维护一个泛型函数指针(如回调),它可以由父级设置,并在必要时由子级调用。像这样:

typedef (*ptFuncF)(float);

class glBase {
public:
  //position,isVisible,virtual mouseDown(x,y),etc
};

class glDerivedChild : public glBase {
public:
  glDerivedChild();
  ~glDerivedChild();

  void changeValue(float fIn) {
    Value = fIn; //ignore these forward declaration errors
    (*callBack)(fIn);
  }

  void setCallBack(ptFuncF pIn) {callBack = pIn;}

  ptFuncF callBack;
  float Value;
};

class glDerivedParent : public glBase {
public:
  glDerivedParent() {
    child = new glDerivedChild();
    child->setCallBack(&onScroll);
  }
  ~glDerivedParent() {delete child;}

  void onScroll(float fIn) {
    //do something
  }

  glDerivedChild* child;
};

class someFoo {
public:
  someFoo() {
    child->setCallBack(&setValue);
  }

  void setValue(float fIn) {
    //do something else
  }

  glDerivedChild child;
};

我对函数指针有点新意,所以我知道我(很明显)做了很多错事。我怀疑它可能涉及类似“typedef(glBase :: * ptFuncF)(float);” 'onScroll(f)'是一个被重写的虚函数,可能有一个通用名称,如'virtual void childCallBack(float)'。我宁愿让解决方案尽可能接近vanilla,所以我想避免像boost这样的外部库。在8小时的大部分时间里,我一直在摸着这个,我希望有人可以提供帮助。谢谢!

4 个答案:

答案 0 :(得分:1)

我认为,你想要的是某种事件或信号机制。

例如,您可以学习如何在 Windows 上组织事件处理。简而言之,您的滚动条在系统中生成新事件,然后系统将其传播到系统中注册的所有元素。

更方便的机制是信号/插槽机制。 BoostQt提供此类工具。 我会推荐这个解决方案

但如果您仍想使用回调,我建议您使用std::functionboost::function)(结合std::bind({{3} }),在需要时)而不是原始函数指针。

答案 1 :(得分:1)

使用boost::function(或std::function(如果有))。像这样(使用你的符号):

typedef std::function<void (float)> ptFuncF; 
//...
void setCallBack(const ptFuncF &pIn);
//...
child->setCallBack(std::bind(&glDerivedParent::onScroll, this, _1)); 
//...
child->setCallBack(std::bind(&someFoo::setValue, this, _1)); 

答案 2 :(得分:1)

指向类的成员函数的函数指针具有以下类型:

<return type> (<class name>::*)(<arguments>)

例如:

typedef void (glBase::*ptFuncF)(float);
        ^^^^
      by the way, you have forgot the `void` in your `typedef`

ptFuncF func = &glDerivedChild::onScroll;

你这样使用它:

glDerivedChild c;
(c.*func)(1.2);

在您的特定示例中,该函数是派生类本身的成员,因此您应该像这样调用它:

(c.*c.callback)(1.2);

内部c.callback是函数指针。其余的如上所述,即:

(class_instance.*function_pointer)(arguments);

您可能还需要查看this question

答案 3 :(得分:-1)

好的,我提出的解决方法有一些额外的开销和分支,但在其他方面是合理的。

基本上,每个回调函数都实现为一个虚拟成员函数,它接收所需的参数,包括指向调用对象的void*指针。每个派生对象还有一个基类指针,该指针引用应该接收它发出的任何事件的对象(通常是它的父节点,但可以是从基类中获取的任何对象)。如果控件有多个子节点,则回调函数使用void*指针来区分它们。这是一个例子:

class glBase {
public:
  virtual onChildCallback(float fIn, void* caller);

  glBase* parent;
};

class glSlider : public glBase {
public:
  glSlider(glBase* parentIn);

  void changeValue(float fIn) {
    Value = fIn;
    parent->onChildCallback(fIn, this);
  }

  float Value;
};

class glButton : public glBase {
public:
  glButton(glBase* parentIn);

  void onClick() {
    parent->onChildCallback(0, this);
  }
};

class glParent : public glBase {
public:
  glParent(glBase* parentIn) : parent(parentIn) {
    childA = new glSlider(this);
    childB = new glButton(this);
  }

  void onChildCallback(float fIn, void* caller) {
    if (caller == childA) {
      //slider specific actions
    } else if (caller == childB) {
      //button specific actions
    } else {
      //generic actions
    }
  }

  glSlider* childA;
  glButton* childB;
};

除了相当少的开销之外,该方案足够灵活,派生类可以忽略某些组件或完全省略它们。我可以稍后回到函数指针的想法(感谢shahbaz),但是两种方案的基础设施的一半都是相同的,额外的开销是最小的,特别是因为控件的数量和种类会相当小。让回调函数使用嵌套响应实际上要好一点,因为你不需要为每个子对象分别使用单独的函数(例如onUpButton,onDownButton等)。