我正在研究大学类(class),并尝试使用GLFW建立一个窗口。
我一直在关注我们要遵循的文档和视频系列(The Cherno在Youtube上使用的Hazel Engine),但是遇到了抽象类问题。
我真的很难同时理解指针和抽象,所以我很难理解我在做什么,但是我相信它正在尝试从Window调用继承的“create”函数来构建WinWindow(之所以这样命名,是因为它是Windows OS特定),但是我在winWindow.cpp的第9行出现了错误C2259“'Engine::WinWindow'无法实例化抽象类”
相关代码如下:
窗口
#pragma once
#include "graphicsContext.h"
#include <string>
#include <functional>
namespace Engine {
class Event; // Be replaced
struct WindowProperties
{
std::string m_title;
unsigned int m_width;
unsigned int m_height;
bool m_isFullScreen;
bool m_isVSync;
WindowProperties(const std::string& title = "My Window", unsigned int width = 800, unsigned int height = 600, bool fullscreen = false) : m_title(title), m_width(width), m_height(height), m_isFullScreen(fullscreen) {}
};
class Window
{
public:
using EventCallbackFn = std::function<void(Event&)>;
virtual void init(const WindowProperties& properties) = 0;
virtual void close() = 0;
virtual ~Window() {};
virtual void onUpdate(float timestep) = 0;
virtual void onResize(unsigned int width, unsigned int height) = 0;
virtual void setVSync(bool VSync) = 0;
virtual void setEventCallback(const EventCallbackFn callback) = 0;
virtual unsigned int getWidth() const = 0;
virtual unsigned int getHeight() const = 0;
virtual void* getNativeWindow() const = 0;
virtual bool isFullScreenMode() const = 0;
virtual bool isVSync() const = 0;
static Window* create(const WindowProperties& properties = WindowProperties());
protected:
std::shared_ptr<GraphicsContext> m_context;
};
}
winWindow.h
#pragma once
#include "windows/window.h"
#include <GLFW/glfw3.h>
namespace Engine {
class WinWindow : public Window {
public:
WinWindow(const WindowProperties& properties);
virtual ~WinWindow();
void onUpdate();// override;
inline unsigned int getWidth() const override { return m_data.width; }
inline unsigned int getHeight() const override { return m_data.height; }
inline void SetEventCallback(const EventCallbackFn& callback) override { m_data.eventCallback = callback; }
void setVSync(bool enabled) override;
bool isVSync() const override;
private:
virtual void init(const WindowProperties& properties);
virtual void shutdown();
GLFWwindow* m_window;
struct windowData {
std::string title;
unsigned int width, height;
bool vSync;
EventCallbackFn eventCallback;
};
windowData m_data;
};
}
winWindow.cpp
#include "engine_pch.h"
#include "Platform/win/winWindow.h"
namespace Engine {
static bool GLFWinit = false;
Window* Window::create(const WindowProperties& properties) {
return new WinWindow(properties);
}
WinWindow::WinWindow(const WindowProperties& properties) {
init(properties);
}
WinWindow::~WinWindow() {
shutdown();
}
void WinWindow::init(const WindowProperties& properties) {
m_data.title = properties.m_title;
m_data.width = properties.m_width;
m_data.height = properties.m_height;
LOG_INFO("Window: {0} - ({1}, {2})", properties.m_title, properties.m_width, properties.m_height);
if (!GLFWinit) {
GLFWinit = true;
}
m_window = glfwCreateWindow((int)properties.m_width, (int)properties.m_height, m_data.title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_window);
glfwSetWindowUserPointer(m_window, &m_data);
setVSync(true);
}
void WinWindow::shutdown() {
glfwDestroyWindow(m_window);
}
void WinWindow::onUpdate() {
glfwPollEvents();
glfwSwapBuffers(m_window);
}
void WinWindow::setVSync(bool enabled) {
if (enabled)
glfwSwapInterval(1);
else
glfwSwapInterval(0);
m_data.vSync = enabled;
}
bool WinWindow::isVSync() const {
return m_data.vSync;
}
}
我还收到一个单独的错误C3668,它显示为“'Engine::WinWindow::SetEventCallback':具有覆盖说明符'override'的方法未覆盖任何基类方法”。尽管我可能会大错特错,但我相信这只是因为目前为止尚未使用。
对于这些问题的任何帮助将不胜感激,但是您是否可以尝试尽可能多地说明自己,以帮助我了解正在发生的事情以及正在发生的事情的决策,因为我真的很努力地遵循所有这些?
最佳答案
您必须实现Window
中WinWindow
中的所有纯虚函数(声明中带有= 0
的那些成员函数)。否则,WinWindow
将是一个抽象类(就像Window
一样),并且无法创建抽象类的实例,但是您正在尝试使用WinWindow
创建new WinWindow(properties)
类型的实例(这就是错误告诉您的内容)。
您尚未重写和实现许多类,例如close
,onResize
等。
您不应忽略其他错误消息。这意味着在WinWindow
中声明函数时您搞砸了。
问题在于Window
中的虚函数具有以下签名:
void setEventCallback(const EventCallbackFn callback)
但是您假定的替代具有签名:
void SetEventCallback(const EventCallbackFn& callback)
函数名称不相同,参数类型也不相同(一个是引用,另一个不是)。覆盖功能必须与它们覆盖的功能的签名匹配。
也不要删除
override
限定词。如果这样做,错误消息将消失,但实际上并不能解决问题。如果要从基类覆盖virtual
函数,请始终添加override
。如果您犯了一个错误,它将产生一个错误,这恰恰是为什么它首先出现的原因。因此,将override
添加到onUpdate
中,并使它与应该重写的virtual
函数相匹配:void onUpdate(float timestep) override;
与问题中提到的问题无关的其他说明:
还要注意,几乎不要在现代C++中直接使用
new
。请改用std::unique_ptr<Window>
和std::make_unique<WinWindow>
。实际上,我不明白为什么您要返回
Window*
而不是WinWindow*
,因此请改用std::unique_ptr<WinWindow>
甚至更好地直接按值返回WinWindow
(不包含任何new
或std::make_unique
)。如果您需要多态性,仍可以在调用站点的智能指针的控制下将其移动到对象中。但是,在这种情况下,您也可以直接使用
WinWindow
的构造函数,而无需通过create
方法。因此,create
毫无意义。还要注意,
inline
关键字是完全多余的。您应该删除它们。他们在那里除了使读者感到困惑之外什么都不做(例如:您知道它们的意思吗?)。直接在类定义中定义的函数是inline
自动。还要注意,您的类
WinWindow
不在rule of 0/3/5后面,因为您的类正在管理资源(窗口句柄),并且为它提供了一个执行不重要操作的析构函数(销毁了窗口),但是您并未实现语义上正确的复制构造函数和复制赋值运算符。因此,如果您偶然隐式或显式复制它,则该类将中断。当您直接将WinWindow
的构造函数用于例如如我上面建议的那样,返回create
中的按值。因此,只要您不解决此问题,就应该坚持使用std::unique_ptr
/ std::make_unique
。在这种情况下,正确的解决方案是使用正确的语义实现move构造函数和move赋值运算符。复制窗口(或至少您实际上不希望这样做)在语义上没有意义,但是您可以将窗口句柄从一个对象移动到另一个对象。
诚然,这些都是相对高级的学科。如果只是为您提供了将C库(GLFW)包装在适当的C++接口(interface)中而不是手动执行的包装器类,则不会出现此问题。我希望您的讲师至少在窗口句柄本身周围提供一个包装器类。如果您还不了解移动语义和资源管理,那么对于初学者来说,正确地做到这一点是困难或不可能的。
我建议您先尝试a good introductory book,它涵盖了所有这些内容,然后再尝试复杂的事情。
关于c++ - 在C++中实现GLFW Windows时,“无法实例化抽象类”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59870228/