C++ 类类型未知时指向非静态成员函数的函数指针?

C++ 类类型未知时指向非静态成员函数的函数指针?,c++,inheritance,callback,function-pointers,C++,Inheritance,Callback,Function Pointers,我正在从事一个游戏项目,该项目的特点是将scratch内置控件渲染到opengl上下文中;按钮、滚动条、列表框等。许多控件都是嵌套的;例如,我的列表框有一个滚动条,滚动条有3个按钮,等等 当滚动条更改值时,我希望它调用响应更改的“some”函数(通常在其父对象中)。例如,如果列表框有一个滑块,它应该实例化该滑块,然后告诉新滑块它应该调用列表框的onScroll(float)函数。所有控件都共享一个公共基类,因此我可以有一个“base*parent”父指针,然后执行“parent->onScrol

我正在从事一个游戏项目,该项目的特点是将scratch内置控件渲染到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;
};
glDerivedChild c;
(c.*func)(1.2);
(c.*c.callback)(1.2);

我对函数指针有点陌生,所以我知道我(显然)做了很多错事。我怀疑它可能涉及类似“typedef(glBase::*ptFuncF)(float);”的内容,其中“onScroll(f)”是一个被重写的虚拟函数,可能具有类似“virtual void childCallBack(float)”的泛型名称。我更愿意让解决方案尽可能接近香草,所以我希望避免像boost这样的外部库。我已经为这件事绞尽脑汁8个小时了,我希望有人能帮上忙。谢谢

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

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

更方便的机制是信号/插槽机制。或者提供这样的工具。我将推荐这个解决方案


但是,如果您仍然只想使用回调,我建议使用()(必要时与())而不是原始函数指针。

使用
boost::function
(或者
std::function
(如果可用)。如下所示(使用您的符号):

typedef std::函数ptFuncF;
//...
void setCallBack(常量ptFuncF&pIn);
//...
child->setCallBack(std::bind(&glderivedpent::onScroll,this,_1));
//...
child->setCallBack(std::bind(&someFoo::setValue,this,_1));

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

<return type> (<class name>::*)(<arguments>)
你是这样使用它的:

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;
};
glDerivedChild c;
(c.*func)(1.2);
(c.*c.callback)(1.2);

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

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;
};
glDerivedChild c;
(c.*func)(1.2);
(c.*c.callback)(1.2);
内部
c.callback
是函数指针。其余部分与上述内容完全相同,即:

(class_instance.*function_pointer)(arguments);

您可能还想看看。

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

基本上,每个回调函数都实现为一个虚拟成员函数,它接收所需的参数,包括一个指向调用对象的
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等)使用单独的函数。

您是否尝试过
typedef(glBase::*ptFuncF)(float)?发生了什么?问题是这似乎只适用于静态成员函数,我需要它调用特定于类实例的函数。这不是真的。我现在正在使用qt,但由于开销和额外的DLL,我计划最终去掉大部分qt内容;例如,我计划稍后用GLUT替换我现在使用的QGLWidget。由于这些控件大多与显示相关,因此我可以使用指向父控件的基类指针,然后调用合适的虚拟函数。至于与主窗口小部件的交互,基类应该足够小,这样我就可以使用多重继承并通过这种方式对其进行篡改。我只是希望函数指针不必如此特定于类型。。。