当前位置: 首页 > 知识库问答 >
问题:

在C中实现GLFW窗口时,不能实例化抽象类

滕弘新
2023-03-14

我正在做大学课程,并试图用GLFW建立一个窗口。我一直在关注我们要关注的文档和视频系列(Youtube上Cherno的Hazel引擎),但遇到了抽象类的问题。

我真的很难理解指针和抽象,所以我真的很难理解我在做什么,但我相信它试图从Window调用继承的“create”函数来构建WinWindow(之所以这样命名是因为它是Windows操作系统特有的),但我在WinWindow的第9行得到一个错误C2259“'Engine::WinWindow'无法实例抽象类”。cpp

有关守则如下:

窗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个答案

杨经武
2023-03-14

必须从WinWindow中的Window实现所有纯虚拟函数(声明中=0的成员函数)。否则,WinWindow将是一个抽象类(与Window一样),无法创建抽象类的实例,但您正试图使用newwinwindow(properties)创建类型为WinWindow的实例(这就是错误告诉您的)。

您还没有重写和实现许多类,例如onResize等。

您不应忽略其他错误消息。这意味着在WinWindow中声明函数时出错。

问题在于窗口中的虚拟函数具有以下签名:

void setEventCallback(const EventCallbackFn callback)

但是你假设的覆盖有签名:

void SetEventCallback(const EventCallbackFn& callback)

函数名不相同,参数类型也不相同(一个是引用,另一个不是)。重写函数必须与其重写的函数的签名相匹配。

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

void onUpdate(float timestep) override;

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

还要注意的是,在现代C语言中几乎不应该直接使用new。使用std::unique\u ptr

事实上,我不明白为什么返回的是Window*,而不是WinWindow*,所以请确保std::unique\u ptr

但是如果是这样的话,您也可以直接使用WinWindow的构造函数,而不是通过创建方法。因此,创建代码是没有意义的。

还要注意,内联关键字是完全多余的。你应该移除它们。它们除了迷惑读者之外什么也不做(例如:你知道它们的意思吗?)。在类定义中直接定义的函数是自动内联的。

还要注意的是,您的类WinWindow没有遵循0/3/5规则,因为您的类正在管理一个资源(窗口句柄),并且您给了它一个析构函数,该析构函数可以执行一些非平凡的操作(销毁窗口),但是您没有实现语义正确的复制构造函数和复制分配操作符。因此,如果您碰巧隐式或显式地复制它,您的类将中断。当您直接使用WinWindow的构造函数时,会发生这种情况,例如,如我上面建议的那样,在create中按值返回。因此,只要你不解决这个问题,你就应该坚持使用std::unique\u ptr/std::make\u unique

在这种情况下,正确的修复方法是使用正确的语义实现move构造函数和move赋值操作符。复制窗口在语义上没有意义(或者至少你实际上不想这么做),但是你可以将窗口句柄从一个对象移动到另一个对象。

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

我建议你在尝试这么复杂的事情之前,先从一本涵盖所有这些内容的好的入门书中学习。

 类似资料:
  • 我查过了,发现最接近的是这个,除了我没有任何向前的声明。我在基类中只有一个纯虚函数,我在子类中实现它,如下所示: 命令H 命令cpp 数字H 数字cpp 发生文件错误: 一个dd.cpp 添加H

  • 我有两个方法和抽象类的接口,它们实现了这个接口并从它重写了一个方法。我可以实例这个抽象类,而不重写接口的其他方法吗?或者我可以用具有另一个签名的方法替换此方法吗? UPD:谢谢你的回答,我真的在我的问题上犯了错误。我是否可以使用匿名类来扩展我的抽象类,而不重写来自实现的所有方法?我如何理解DragonK的回答,不,我需要创建类,这将是扩展抽象类和重写其他方法?

  • 我正在尝试实现上面提到的解决方案,但是在这里,我得到了一个错误“RowMapper是抽象的,不能实例化”和“表达式的非法开始”。下面正是我所说的

  • 我正在学习OOP的概念。在阅读继承的过程中,我了解到在初始化子类之前必须先初始化超类,即所有超类的构造函数必须在子类构造函数之前运行。此外,我们还可以直接创建超级类的实例。对于例如。 现在,我遇到了抽象类。看来我们不能实例化一个抽象类。要使用抽象类,您必须从另一个类继承它,并为其中的抽象方法提供实现。 我的问题是,在实例化具体子类的同时,抽象超类的构造函数会在具体子类的构造函数之前被调用。如果是这

  • 本文向大家介绍C#实现简单的Login窗口实例,包括了C#实现简单的Login窗口实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了C#实现简单的Login窗口。分享给大家供大家参考。具体实现方法如下: C# 制作登录窗体,登录成功之后正确的做法是关闭(close)登录窗体,而不是隐藏窗体(hide) FrmLogin窗体: Main方法入口: 希望本文所述对大家的C#程序设计有所帮助。

  • 问题内容: 在Java中,可以扩展带有匿名类的接口,该类可以动态实现。例: (更多信息:http : //www.techartifact.com/blogs/2009/08/anonymous-classes-in-java.html#ixzz1k07mVIeO) 这在C#中可能吗?如果不是,那么有什么可行的替代方法而不必依赖于实现过多的子类? 问题答案: 不,您不能在C#中做到这一点-但通常,