«Не удается создать абстрактный класс» при реализации GLFW Windows в C ++ - PullRequest
1 голос
/ 23 января 2020

Я работаю над университетской курсовой работой и пытаюсь построить окно, используя GLFW. Я следил за документацией и видео сериалами, которым нам дали следовать (The Hazel Engine от The Cherno на Youtube), но столкнулся с проблемой с абстрактными классами.

Мне очень трудно понять оба указателя и абстракция, поэтому я действительно изо всех сил пытаюсь понять, что я делаю, но я полагаю, что он пытается вызвать унаследованную функцию 'create' из Window, чтобы построить WinWindow (названное так, потому что это Windows OS speci c), но я получаю ошибку C2259 "Engine :: WinWindow" не может создать экземпляр абстрактного класса "в строке 9 winWindow. cpp

Соответствующий код следует:

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 ': метод со спецификатором переопределения' переопределить 'не переопределял методы базового класса ". Хотя я могу быть совершенно неправ, я считаю, что это только потому, что на данный момент оно не используется.

Любая помощь по этим вопросам будет принята с благодарностью, но не могли бы вы попытаться объяснить себя как можно больше, чтобы помогите мне понять, что происходит, и принимать решения о том, что происходит, поскольку я действительно изо всех сил стараюсь следить за всем этим?

1 Ответ

4 голосов
/ 23 января 2020

Вы должны реализовать все чисто виртуальные функции из Window в WinWindow (те функции-члены с = 0 в объявлении). В противном случае WinWindow будет абстрактным классом (как Window) и экземпляры абстрактных классов не могут быть созданы, но вы пытаетесь создать экземпляр типа WinWindow с new WinWindow(properties) (который это то, что говорит вам ошибка).

Вы не переопределили и не реализовали многие классы, например close, onResize, et c.

Вы должны не игнорировать другое сообщение об ошибке. Это означает, что вы ошиблись при объявлении функции в WinWindow.

Проблема в том, что виртуальная функция в Window имеет такую ​​подпись:

void setEventCallback(const EventCallbackFn callback)

Но ваше предполагаемое переопределение имеет подпись:

void SetEventCallback(const EventCallbackFn& callback)

Имена функций не совпадают, а типы параметров также не одинаковы (один является ссылкой, другой - нет). Переопределяющие функции должны соответствовать сигнатуре переопределяемых ими функций.

Не сбрасывайте и квалификатор override. Если вы сделаете это, сообщение об ошибке исчезнет go, но фактически не решит проблему. Если вы переопределяете функцию virtual из базового класса, всегда добавляйте override. Тот факт, что это даст ошибку, если вы допустили ошибку, это именно то, почему она существует в первую очередь. Поэтому добавьте override к onUpdate и сделайте так, чтобы он соответствовал функции virtual, которую он также должен переопределять:

void onUpdate(float timestep) override;

Дополнительные примечания, не связанные с проблемой, упомянутой в вопросе:


Также обратите внимание, что вы почти никогда не должны использовать new непосредственно в современном C ++. Вместо этого используйте 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 не следует правилу 0/3/5 , потому что ваш класс управляет ресурсом (дескриптором окна), и вы дали ему деструктор, который делает что-то нетривиальное (уничтожение окна), но вы не реализовали семантически правильный конструктор копирования и оператор присваивания копии. Следовательно, ваш класс сломается, если вы когда-нибудь явно или неявно скопируете его. Это происходит, в частности, когда вы используете конструктор WinWindow напрямую, например, для возврата по значению в create, как я предлагал выше. Поэтому до тех пор, пока вы не исправите это, вы должны придерживаться std::unique_ptr / std::make_unique.

. В этом случае правильным решением будет реализация конструктора move и move оператор присваивания с правильной семантикой. Семанти c не имеет смысла копировать Окно (или, по крайней мере, вы на самом деле этого не хотите), но вы можете переместить дескриптор окна из одного объекта в другой.


По общему признанию, это относительно продвинутые предметы. Эта проблема не возникла бы, если бы вы просто предоставили класс-оболочку, который оборачивает библиотеку C (GLFW) в надлежащий интерфейс C ++ вместо того, чтобы делать это вручную. Я ожидаю, что ваш инструктор предоставит класс-оболочку, по крайней мере, вокруг самого дескриптора окна. Для новичка сделать это трудно или невозможно, если вы еще не изучили семантику перемещения и управление ресурсами.

Я предлагаю вам изучить у хорошую вводную книгу , которая охватывает все это, прежде чем вы попробуете что-то столь сложное.

...