С++ Странное нарушение прав доступа с OpenGL

Я новичок в C++, поэтому надеюсь, что здесь мне помогут. Я пытаюсь перенести свой игровой движок на C++, но C++ ведет себя немного... "Странно". Следующая ситуация:

если я запускаю 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;
}

Если я запускаю 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()
{

}

Теперь вдруг я получаю «Нарушение прав доступа». Вопрос в том, почему? Файл "base.h" просто содержит

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

и классы Window и TestShader не должны иметь значения, потому что они работают в первых двух примерах. Как я уже говорил, я новичок в C++ и просто не понимаю, почему это не работает. Не могли бы вы помочь мне выяснить хотя бы, почему это не работает, или лучше помочь мне решить проблему.

Это моя вторая попытка получить полезный ответ от StackOverflow, задав вопрос. Пожалуйста, сделайте мне одолжение. Пожалуйста, прочтите ситуацию, прежде чем пометить этот вопрос как дубликат. В прошлый раз, когда это не было дубликатом, проблема была совсем другой.

Изменить

По запросу сообщение об ошибке (извините, я на работе, поэтому язык немецкий)

Определить значение по адресу 0x0126489D в GLFWGame.exe: 0xC0000005: Предварительное определение по адресу 0xCCCCEA4.

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

И я постараюсь сократить код до самого важного.


person picatrix    schedule 28.12.2016    source источник
comment
Правильный инструмент для решения таких проблем — ваш отладчик. Вы должны выполнить свой код построчно, прежде чем задать вопрос о переполнении стека. Для получения дополнительной помощи прочитайте Как отлаживать небольшие программы (по Эрик Липперт). Как минимум, вы должны [отредактировать] свой вопрос, включив в него минимальный, полный и проверяемый пример, который воспроизводит вашу проблему, а также наблюдения, которые вы сделали в отладчике.   -  person πάντα ῥεῖ    schedule 28.12.2016
comment
Можете ли вы опубликовать фактическое сообщение об ошибке?   -  person AresCaelum    schedule 28.12.2016
comment
из того, что я вижу, я удивлен, что вы не получили нарушение прав доступа в своем первом тестовом примере. Вы устанавливаете указатели на переменные, выходящие за пределы области видимости, в конце вашей функции DemoGame::Init(). Это означает, что они очищаются и вызывают неопределенное поведение.   -  person AresCaelum    schedule 28.12.2016


Ответы (2)


Вы сохраняете адреса объектов стека, которые удаляются. Например,

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

Создает в стеке локальную переменную wi, которая автоматически удаляется, когда выходит за пределы области видимости (что происходит в конце функции). После этого w будет указывать на уже освобожденный адрес, что приведет к большим неприятностям, когда вы попытаетесь получить доступ к этим переменным позже, как здесь:

glfwSwapBuffers(w->getWindow());

Если вы хотите создать объект окна в куче, вы должны использовать следующий код в DemoGame::init():

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

Не забудьте удалить этот объект вручную, вызвав delete w, когда он вам больше не нужен. Та же проблема возникает и для экземпляра TestShader.

Примечание. Window wi = Window(800, 600, "test"); по-прежнему является странным синтаксисом при создании объектов в стеке. Правильный способ: Window wi(800, 600, "test"); Посмотрите на эти сообщения, почему это имеет значение: Calling конструкторы в c++ без new

Изменить: ваш первый пример работает просто потому, что вы вызываете функцию рендеринга внутри функции инициализации, поэтому объекты не выходят за рамки. Хранение указателей на локальные объекты по-прежнему не является хорошей практикой.

person BDL    schedule 28.12.2016
comment
Следует также упомянуть, что если он вызывает новый, ему нужно использовать удаление, или он может использовать shared_ptr - person AresCaelum; 28.12.2016
comment
Оно работает! спасибо! Это будет самый трудный путь к обучению, по которому я когда-либо ходил... - person picatrix; 28.12.2016

Ваша проблема здесь:

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

И экземпляр Window, и экземпляр TestShader являются локальными переменными, которые будут очищены, как только они выйдут из области действия (конец инициализации), и, следовательно, запоминание их адресов не имеет смысла. Вам нужно будет создавать эти экземпляры динамически (new) или настраивать их в определении вашего класса.

person Till    schedule 28.12.2016