OpenGL 的 C++ 奇怪访问冲突

C++ Strange Access Violation with OpenGL

我是 C++ 的新手,所以我希望能在这里得到一些帮助。 我尝试将我的游戏引擎移植到 C++,但 C++ 的行为有点……"Strange"。 以下情况:

如果我 运行 test1() 一切正常。

main.cpp

#include <iostream>

#include "../headers/base.h"

#include "../headers/DemoGame.h"
#include "../headers/TestShader.h"

using namespace std;
using namespace engine;


void run(TestShader* t, GLuint VAO, GLFWwindow* w)
{
    glfwPollEvents();


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(t->progID);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);

    glfwSwapBuffers(w);
}

void test1()
{
    Window w = Window(800, 600, "test");
    TestShader t = TestShader();
    GLuint VAO, VBO;

    GLfloat vertices[9] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };


    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

    while (!glfwWindowShouldClose(w.getWindow()))
    {
        run(&t, VAO, w.getWindow());
    }


}

void test2()
{
    DemoGame game = DemoGame();
    game.start();
}

int main()
{

    test1();

    return 0;
}

如果我 运行ning test2() 涉及以下内容 类:

Engine.h

#pragma once

#ifndef H_ENGINE
#define H_ENGINE

#include "base.h"

namespace engine
{
    class Engine
    {
        private:
            bool running;

        public:
            void start()
            {
                init();
                process();
            }

            void stop()
            {
                this->running = false;
            }

        private:
            void process()
            {
                update();
            }

        public:
            virtual void init() = 0;
            virtual void update() = 0;
            virtual void render() = 0;
            virtual void terminate() = 0;
    };
}

#endif

DemoGame.h

#pragma once

#ifndef DEMO_DEMO_GAME
#define DEMO_DEMO_GAME

#include "base.h"

#include "Window.h"
#include "Engine.h"
#include "TestShader.h"


using namespace engine;

class DemoGame : public Engine
{

    public:
        Window* w;
        TestShader* t;

        GLuint VBO, VAO;

    public:
        DemoGame() : Engine() { }

    public:
        void init();

        void update();

        void render();

        void terminate();
};

#endif

DemoGame.cpp

#include "../headers/DemoGame.h"
#include <iostream>

using namespace std;

void DemoGame::init()
{
    cout << "ping" << endl;

    Window wi = Window(800, 600, "test");
    w = &wi;
    TestShader te = TestShader();
    t = &te;



    GLfloat vertices[9] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };


    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

    while (!glfwWindowShouldClose(w->getWindow()))
    {
        render();
    }
}

void DemoGame::update()
{

}

void DemoGame::render()
{
    glfwPollEvents();


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(t->progID);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);

    glfwSwapBuffers(w->getWindow());
}

void DemoGame::terminate()
{

}

它也有效。但是正如您所见,Engine.h 应该控制主循环。如果我稍微更改一下代码:

Engine.h

#pragma once

#ifndef H_ENGINE
#define H_ENGINE

#include "base.h"

namespace engine
{
    class Engine
    {
        private:
            bool running;

        public:
            void start()
            {
                init();

                running = true;

                while (running)
                {
                    process();
                }

            }

            void stop()
            {
                this->running = false;
            }

        private:
            void process()
            {
                update();
            }

        public:
            virtual void init() = 0;
            virtual void update() = 0;
            virtual void render() = 0;
            virtual void terminate() = 0;
    };
}

#endif

DemoGame.cpp

#include "../headers/DemoGame.h"
#include <iostream>

using namespace std;

void DemoGame::init()
{
    cout << "ping" << endl;

    Window wi = Window(800, 600, "test");
    w = &wi;
    TestShader te = TestShader();
    t = &te;



    GLfloat vertices[9] = {
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f,
        0.0f,  0.5f, 0.0f
    };


    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glBindVertexArray(0);

}

void DemoGame::update()
{
    render();
}

void DemoGame::render()
{
    glfwPollEvents();


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(t->progID);
    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(0);

    glfwSwapBuffers(w->getWindow());
}

void DemoGame::terminate()
{

}

现在我突然得到一个"Access Violation"。问题是为什么? 文件 "base.h" 只包含

#define GLEW_STATIC
#include "GL/glew.h"
#include "GLFW/glfw3.h"

和 类 Window 和 TestShader 应该无关紧要,因为它们在前两个示例中起作用。正如我之前所说,我是 C++ 的新手,我只是不明白为什么它不起作用。你能帮我找出至少为什么不起作用或更好地帮助我解决问题吗?

这是我第二次尝试通过发布问题从 Whosebug 获得有用的答案。请帮我一个忙。在将此问题标记为重复之前,请考虑阅读情况。上一次它不是重复的,问题却大不相同。

编辑

根据错误消息的要求(对不起,我在工作,所以语言是德语)

Ausnahme ausgelöst bei 0x0126489D in GLFWGame.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0xCCCCCEA4.

Falls ein Handler für diese Ausnahme vorhanden ist, kann das Programm möglicherweise weiterhin sicher ausgeführt werden.

我会尽量将代码缩短到最重要的部分。

您存储被删除的堆栈对象的地址。例如,

Window wi = Window(800, 600, "test");
w = &wi;

在堆栈上创建一个局部变量 wi,当它超出范围时会自动删除(函数结束时就是这种情况)。在那之后,w 将指向一个已经被释放的地址,这将在您稍后尝试访问此变量时导致大麻烦,就像您在这里所做的那样:

glfwSwapBuffers(w->getWindow());

如果要在堆上创建window对象,则必须在DemoGame::init()中使用如下代码:

w = new Window(800, 600, "test");

不要忘记在不再需要时通过调用 delete w 手动删除该对象。 TestShader 实例也出现同样的问题。

旁注: Window wi = Window(800, 600, "test"); 在堆栈上创建对象时仍然是一种奇怪的语法。正确的方法是 Window wi(800, 600, "test"); 看看这个帖子为什么会有所不同:Calling constructors in c++ without new

编辑:您的第一个示例之所以有效,是因为您在 init 函数内调用渲染函数,因此对象不会超出范围。存储指向本地对象的指针仍然不是好的做法。

您的问题在这里:

Window wi = Window(800, 600, "test");
w = &wi;
TestShader te = TestShader();
t = &te;

Window 的实例和 TestShader 的实例都是局部变量,一旦超出范围(init 结束)就会被清理,因此要记住他们的地址没有意义。您将需要动态创建这些实例 (new) 或在您的 class 定义中设置它们。