'Cannot Instantiate Abstract Class' 在 C++ 中实现 GLFW Windows

'Cannot Instantiate Abstract Class' when implementing GLFW Windows in C++

我正在攻读大学课程并尝试使用 GLFW 构建 window。 我一直在关注我们被要求关注的文档和视频系列(The Cherno 在 Youtube 上的 The Hazel Engine),但是 运行 遇到了抽象 classes.

的问题

我真的很难理解指针和抽象,所以我真的很难理解我在做什么,但我相信它试图调用从 Window 继承的 'create' 函数,以构建一个 WinWindow(如此命名是因为它是 Windows OS 特定的),但我收到错误 C2259“'Engine::WinWindow' 无法实例化抽象 class”在 winWindow.cpp

的第 9 行

相关代码如下:

window.h

#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' 的方法没有覆盖任何基础 class 方法”。虽然我可能大错特错,但我相信这只是因为它现在还未使用。

对于这些问题的任何帮助将不胜感激,但您能否尝试尽可能多地解释自己以帮助我了解正在发生的事情以及正在发生的事情的决策,因为我真的很难理解所有这个?

必须实现WinWindowWindow的所有纯虚函数(那些在声明中带有= 0的成员函数)。否则 WinWindow 将是一个 抽象 class (因为 Window 是)并且无法创建抽象 classes 的实例,但是你正在尝试使用 new WinWindow(properties) 创建类型 WinWindow 的实例(这是错误告诉您的内容)。

您还没有覆盖和实施许多 classes,例如closeonResize

您应该而不是忽略其他错误消息。这意味着你在 WinWindow.

中声明函数时搞砸了

问题是 Window 中的虚函数有这个签名:

void setEventCallback(const EventCallbackFn callback)

但是您假定的覆盖具有签名:

void SetEventCallback(const EventCallbackFn& callback)

函数名不一样,参数类型也不一样(一个是引用,一个不是)。覆盖函数必须匹配它们正在覆盖的函数的签名。

也不要删除 override 限定符。如果这样做,错误消息会消失,但实际上并不能解决问题。如果您从基础 class 覆盖 virtual 函数,请始终添加 override。如果你犯了错误,它会给出一个错误,这正是它首先存在的原因。因此,将 override 添加到 onUpdate 并使其与应该覆盖的 virtual 函数匹配:

void onUpdate(float timestep) override;

与问题中提到的问题无关的进一步说明:


另请注意,您几乎不应该在现代 C++ 中直接使用 new。请改用 std::unique_ptr<Window>std::make_unique<WinWindow>

事实上,我不明白你为什么 returning Window* 而不是 WinWindow*,所以把 std::unique_ptr<WinWindow> 改成 std::unique_ptr<WinWindow> 甚至更好 return WinWindow 直接按值(没有任何 newstd::make_unique)。如果您需要多态性,您仍然可以在调用站点将其移动到智能指针控制下的对象中。

但如果是这种情况,您也可以直接使用 WinWindow 的构造函数,而不是通过 create 方法。所以 create 有点没有意义。


另请注意,inline 关键字完全是多余的。你应该删除它们。他们在那里除了混淆 reader 什么都不做(例如: you 知道他们的意思吗?)。 class 定义中直接定义的函数自动 inline


另请注意,您的 class WinWindow 未遵循 rule of 0/3/5,因为您的 class 正在管理资源(window 句柄)并且你给了它一个析构函数来做一些不平凡的事情(破坏 window),但你没有实现语义正确的复制构造函数和复制赋值运算符。因此,如果您碰巧隐式或显式复制它,您的 class 将会中断。当您直接将 WinWindow 的构造函数用于例如return 按我上面建议的 create 中的值。所以只要你不解决这个问题,你就应该坚持使用 std::unique_ptr/std::make_unique.

在这种情况下,正确的解决方法是使用正确的语义实现 move 构造函数和 move 赋值运算符。 copy a Window 没有语义意义(或者至少你实际上不想这样做),但你可以 move window 从一个对象到另一个对象的句柄。


诚然,这些都是相对高级的科目。如果您只是提供了一个将 C 库 (GLFW) 包装在适当的 C++ 接口中而不是手动执行此操作的包装器 class,则不会出现此问题。我希望您的讲师提供包装器 class,至少围绕 window 句柄本身。如果您还没有了解移动语义和资源管理,那么正确地做到这一点对于初学者来说是困难的或不可能的。

我建议你先学习 a good introductory book ,它涵盖了所有这些,然后再尝试如此复杂的事情。