Недавно я много читал и решил попробовать реализовать простой шаблон состояния, чтобы попытаться посмотреть, как вещи сочетаются друг с другом.
Однако я столкнулся с проблемой, с которой никогда раньше не сталкивался: подкласс сцены (Start), похоже, не наследует значение указателя, установленного конструктором суперкласса.
Вот код
//main.cpp
#include "Game/game.hpp"
#include "Game/Scene/start.hpp"
int main()
{
Game::Game *g = new Game::Game;
Game::Scene::Start *s = new Game::Scene::Start(g);
g->set_scene(s);
g->loop();
delete g;
return 0;
}
//Game/Scene/Scene.hpp
#ifndef GAME_SCENE_H
#define GAME_SCENE_H
namespace Game
{
class Game;
namespace Scene
{
class Scene
{
public:
Scene(Game *game);
virtual ~Scene();
virtual void handle_input() = 0;
virtual void update();
protected:
Game *game;
};
}
}
#endif
//Game/Scene/Scene.cpp
#include "scene.hpp"
#include <iostream>
Game::Scene::Scene::Scene(Game *game): game(game)
{
std::cout << "Game::Scene::Scene::Scene() > game: " << this->game << std::endl;
}
Game::Scene::Scene::~Scene() {}
void Game::Scene::Scene::update()
{
std::cout << "Game::Scene::Scene::update(): game: " << game << std::endl;
}
//Game/Scene/start.hpp
#ifndef GAME_SCENE_START_H
#define GAME_SCENE_START_H
#include "scene.hpp"
namespace Game
{
namespace Scene
{
class Start:
public Scene
{
public:
Start(Game *game);
void handle_input() override;
void update() override;
protected:
Game *game;
};
}
}
#endif
//Game/Scene/start.cpp
#include "start.hpp"
#include "../game.hpp"
#include <iostream>
Game::Scene::Start::Start(Game *game): Scene(game) {
std::cout << "Game::Scene::Start::Start() > game: " << this->game << std::endl;
}
void Game::Scene::Start::handle_input()
{}
void Game::Scene::Start::update()
{
Scene::update();
std::cout << "Game::Scene::Start::update(): game: " << game << std::endl;
game->set_keep_going(false);
}
//Game/game.hpp
#ifndef GAME_H
#define GAME_H
#include "Scene/scene.hpp"
namespace Game
{
class Game
{
public:
Game();
void loop();
void set_keep_going(bool state);
void set_scene(Scene::Scene *scene);
private:
bool keep_going;
Scene::Scene *current_scene;
};
}
#endif
//Game/game.cpp
#include "game.hpp"
#include <iostream>
Game::Game::Game():
keep_going(true),
current_scene(nullptr)
{}
void Game::Game::loop()
{
while(keep_going && current_scene)
{
current_scene->handle_input();
current_scene->update();
}
return;
}
void Game::Game::set_keep_going(bool state)
{
keep_going = state;
}
void Game::Game::set_scene(Scene::Scene *scene)
{
if (scene)
{
if (current_scene)
{
delete current_scene;
}
current_scene = scene;
}
}
И вот результат, который он генерирует:
Game::Scene::Scene::Scene() > game: 00000210FA38B330
Game::Scene::Start::Start() > game: 0000000000000000
Game::Scene::Scene::update(): game: 00000210FA38B330
Game::Scene::Start::update(): game: 0000000000000000
Segmentation fault
Если я удалю часть this->, которую я использовал в конструкторе подкласса для отладки, вывод станет таким
Game::Scene::Scene::Scene() > game: 000001D247B5B370
Game::Scene::Start::Start() > game: 000001D247B5B370
Game::Scene::Scene::update(): game: 000001D247B5B370
Game::Scene::Start::update(): game: 0000000000000000
Segmentation fault
Что только делает меня еще более странным, что он внезапно решил перезагрузить себя.
Это, без сомнения, невероятно простая вещь, которую я упускаю из виду, или какие-то махинации с указателями, с которыми я никогда раньше не сталкивался, но я в тупике от того, что здесь может происходить.
Любая помощь или отзывы о моем коде будут оценены.
У вас есть две переменные-члены Scene::game
и Start::game
. Вы никогда не ставили Start::game
. По умолчанию game
разрешается в Scene::game
, а переменная базового класса затеняется. Включите предупреждения компилятора, может компилятор об этом предупредит, в данном конкретном случае не уверен.
Исправить:
Game::Scene::Start::Start(Game *game): Scene(game), game(game)
{
std::cout << "Game::Scene::Start::Start() > game: " << this->game << std::endl;
}
Или просто удалите Start::game
и используйте участника из Scene
. Это, вероятно, то, что вы имели в виду в первую очередь, отсюда и квалификатор protected
.
Если вы хотите получить обратную связь - не используйте new
, вместо этого используйте std::unique_ptr
. А еще лучше вообще не использовать ptrs, это не Java.
@DangerDaisy Текущие рекомендации C++ настоятельно рекомендуют использовать интеллектуальные указатели для владения, а не необработанные указатели, они (или ссылки) должны указывать на невладеющий указатель - указатель, который никогда не отвечает за освобождение и имеет средства для определения того, жива ли память. Последнее можно сделать на основе логических ограничений программы. Например. вы знаете, что Game
всегда переживет сцены, поэтому они могут иметь необработанные указатели на него. Конечно, вы можете использовать указатели, извините за не столь четкую формулировку.
Все в порядке. Совсем не использовать указатели казалось немного странным. Я знаю, что мне, вероятно, следует использовать интеллектуальные указатели, но я всегда сталкивался с проблемами, используя их так или иначе, поэтому я склонен прибегать к старомодному способу. Хорошо, проведите некоторое тестирование правильной процедуры их использования.
Ага, это было. Должно быть, вы забыли удалить защищенный материал после копирования класса. Наше любопытство: под «Не использовать указатели» вы имеете в виду только необработанные указатели? Наши просто указатели вообще?