我正在研究大学类(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'的方法未覆盖任何基类方法”。尽管我可能会大错特错,但我相信这只是因为目前为止尚未使用。

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

最佳答案

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

您尚未重写和实现许多类,例如closeonResize等。

您不应忽略其他错误消息。这意味着在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(不包含任何newstd::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/

10-12 04:02
查看更多