Указатель члена суперкласса становится нулевым при доступе к подклассу

Недавно я много читал и решил попробовать реализовать простой шаблон состояния, чтобы попытаться посмотреть, как вещи сочетаются друг с другом.

Однако я столкнулся с проблемой, с которой никогда раньше не сталкивался: подкласс сцены (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

Что только делает меня еще более странным, что он внезапно решил перезагрузить себя.

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

Любая помощь или отзывы о моем коде будут оценены.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
119
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

У вас есть две переменные-члены 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 14.12.2020 20:25

@DangerDaisy Текущие рекомендации C++ настоятельно рекомендуют использовать интеллектуальные указатели для владения, а не необработанные указатели, они (или ссылки) должны указывать на невладеющий указатель - указатель, который никогда не отвечает за освобождение и имеет средства для определения того, жива ли память. Последнее можно сделать на основе логических ограничений программы. Например. вы знаете, что Game всегда переживет сцены, поэтому они могут иметь необработанные указатели на него. Конечно, вы можете использовать указатели, извините за не столь четкую формулировку.

Quimby 14.12.2020 20:54

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

DangerDaisy 14.12.2020 21:06

Другие вопросы по теме