我的简短回答是:
使用std::function 代替原始函数指针。这为您提供了更大的灵活性,因为它可以容纳:
- 函数指针
- 方法指针(当然是对象)
- 函子
- lambdas(有或没有实际重复上述一个或另一个的捕获)。
您仍然受限于为调用定义签名,但您可以提供回调上下文,这可能就是您所需要的。
所以,这就是它的样子:
#include <functional>
class Window
{
public:
Window();
~Window();
void WindowLoop(std::function<void()> f);
protected:
GLFWwindow* window;
};
void Window::WindowLoop(std::function<void()> f)
{
while(!glfwWindowShouldClose(window))
{
f();
glfwPollEvents();
}
}
(它看起来与 OP 的原始样本没有太大区别。)
三思而后行,我发现值得一提的是小部件集以及它们提供的解决方案(因为要解决相同的问题)。
两个通用解决方案是
信号基本上只是一个带有函数指针的容器(或std::function 或类似的东西)。在某些情况下会发出信号(即调用存储的函数指针)。因此,其他对象可以在这种情况下得到通知(通过在信号中注册它们的信号处理程序)。所以,一个信号实际上和上面类似,只是函数指针不是临时提供的,而是存储在成员变量中。
替代方法是在某些情况下调用virtual 方法。要添加自定义行为,resp。必须派生基类,并且必须覆盖 quest 中的 virtual 方法。
在 OP 的情况下,可能如下所示:
class Window
{
public:
Window();
~Window();
void WindowLoop();
protected:
virtual void step();
protected:
GLFWwindow* window;
};
void Window::WindowLoop()
{
while(!glfwWindowShouldClose(window))
{
step();
glfwPollEvents();
}
}
void Window::step() { /* empty placeholder */ }
要在应用程序中使用它,Window 的派生类是必需的:
class GameWindow: public Window {
protected:
virtual void step() override;
};
void GameWindow::step()
{
// Do the game step stuff (e.g. rendering)
// where this (of type GameWindow) can provide the necessary context.
}
关于Qt,有各种情况的信号和virtual 方法,例如小部件中的事件处理程序。大多数情况下都可以选择非此即彼——我不记得两者都可用于某事。例如。可以为QPushButton::clicked() 注册信号处理程序,但要将事件处理程序自定义为mousePressEvent(),必须重载Qt 小部件以覆盖事件处理程序方法。 (还有事件过滤器的概念,但恕我直言,这并不完全相同。)
gtkmm 代替(至少在我过去使用的版本 2 中)提供了我能记住的所有 virtual 方法 和 信号。因此,总是可以选择派生 gtkmm 小部件或仅通过注册信号处理程序来更改/扩展 gtkmm 小部件的行为。这可能会带来很少的额外性能成本,但对于应用程序编程来说非常方便。