作为练习,我正在用 C++ 围绕 GLFW 和 OpenGL 编写一个简单的包装器。我有两个类:
Window
和Renderer
,其中Window
拥有Renderer
的实例。
Window
类在其构造函数中设置GLFW上下文,运行主循环,并使用其Renderer
成员来处理绘图。 Renderer
类在其构造函数中设置缓冲区并处理 OpenGL 函数调用。
我面临的问题是 OpenGL 需要任何函数调用的有效上下文。如果我在调用
Renderer
构造函数之前初始化 Window
,则 Renderer
构造函数将在创建 GLFW 上下文之前运行。
为了避免这种情况,我将
unique_ptr
存储到 Renderer
类中的 Window
,并在 std::make_unique<Renderer>
构造函数中使用 Window
实例化它。然后,在 Window
析构函数中,我在销毁 GLFW 上下文之前调用 std::unique_ptr::reset()
,以确保在上下文有效时仍然可以进行 OpenGL 调用来释放资源。
class Window
{
public:
Window()
{
// Initializing GLFW and creating an OpenGL context
// ...
m_renderer = std::make_unique<Renderer>();
}
~Window()
{
m_renderer.reset();
glfwTerminate();
}
int run()
{
while(...)
{
m_renderer->draw();
// ...
}
return 0;
}
private:
std::unique_ptr<Renderer> m_renderer;
// ...
}
class Renderer
{
public:
Renderer() { /* Set buffers */ }
~Renderer() { /* Free buffers */ }
draw() { glDrawElements(...); /* ... */ }
private:
// ...
}
int main()
{
Window window();
return window->run();
}
我理解,在调用
Renderer
构造函数时,Window
对象应该已经初始化,但这里的情况并非如此。我觉得我可能颠倒了 Renderer
和 Window
之间的依赖关系,或者我的整体架构可能是错误的。我宁愿依赖根据范围在正确的时刻调用构造函数和析构函数,而不是手动触发它。
更好的解决方案是什么?
我建议您创建一个单独的类,将其命名为
GLFWInit
,它执行 GLFW 初始化并在其析构函数中调用 glfwTerminate()
。那么你有两个选择:
将
GLFWInit
类型的对象嵌入到您的Window
类中。尽早放置该成员,但至少在 m_renderer
成员之前。
从
Window
派生您的 GLFWInit
类。
这两种方法都确保 GLFW 在
m_renderer
之前初始化并在其之后拆除。然后,您甚至不必将渲染器设置为指针,并且可以直接嵌入成员(如果可行)。