我正在开发一个游戏项目,该项目具有在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小时的大部分时间里,我一直在摸着这个,我希望有人可以提供帮助。谢谢!
答案 0 :(得分:1)
我认为,你想要的是某种事件或信号机制。
例如,您可以学习如何在 Windows 上组织事件处理。简而言之,您的滚动条在系统中生成新事件,然后系统将其传播到系统中注册的所有元素。
更方便的机制是信号/插槽机制。 Boost或Qt提供此类工具。 我会推荐这个解决方案。
但如果您仍想使用回调,我建议您使用std::function(boost::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等)。