Я сохраняю состояния моей игры (по сути, коллекции сущностей) в векторе общих указателей. При добавлении состояний в вектор производная часть состояний теряется, и они возвращаются к классу базовых состояний. Все компилируется нормально, но когда я запрашиваю имена состояний, все они возвращаются как DEFAULT_STATE_NAME. Я прочитал много информации о разделении объектов, но не могу понять, что здесь не так.

< Сильный > State.hpp

class State {

protected:

    Game &game;

public:

    typedef shared_ptr<State> Pointer;

    static const StateName name = DEFAULT_STATE_NAME;

    explicit State(Game &game_) : game(game_) ;

    virtual ~State() {}

};

Пример производного класса состояний

namespace {

class Overworld : public State {

public:

    static const StateName name;

    Overworld(Game &game) : State(game) {}

};

const StateName Overworld::name = OVERWORLD;

}

< Сильный > Game.hpp

class Game {

private:

    vector<State::Pointer> states;

public:

    void addState(const State::Pointer &state) {
        if(!state)
            throw "invalid state error";

        states.push_back(state);
    }

    // ...

}
-1
Inigo Selwood 23 Окт 2018 в 23:39

2 ответа

Лучший ответ

Чтобы получить доступ к методам -членам производного класса через указатель (или ссылку) на его базовый класс, вы должны использовать полиморфизм (чего вы не сделали). Например

struct Base {
    virtual string name() const { return "Base"; }
};

struct Derived : Base {
    string name() const override { return "Derived"; }
};

const Base*ptr = new Derived;
assert(ptr->name()=="Derived");

Такой полиморфизм работает только с нестатическими методами-членами , но не с элементами данных или статическими функциями-членами. В вашем случае полиморфизма нет и, следовательно, Base::name остается, ну, Base::name.

Однако в вашем конкретном случае есть два других возможных решения. Во-первых, вы можете использовать RTTI, хотя это обычно не одобряется. Другой вариант - сохранить name как член данных в Base и передать его при построении:

struct Base {
    const string name = "Base";
    Base() = default;
  protected:
    Base(string const&n)
    : name(n) {}
};

struct Derived : Base {
    Derived()
    : Base("Derived") {}
};

const Base*ptr = new Derived;
assert(ptr->name=="Derived");

Когда нет никакого полиморфизма (и, следовательно, нет виртуальной таблицы и дополнительных косвенных обращений), но за счет элемента данных name.

2
Walter 23 Окт 2018 в 21:22

name в State и name в Overworld - две полностью независимые переменные класса. Они не являются частью какого-либо состояния экземпляра, и вы не можете напрямую запросить экземпляр для переменных класса, поскольку они не могут быть virtual. Чтобы получить доступ к переменным класса полиморфно, вам необходимо использовать виртуальную функцию.

Добавьте такую функцию-член в State и не забудьте при необходимости переопределить ее в производных классах. Или, знаете ли, вы можете просто использовать стандартные языки RTTI, используя typeid .

0
Deduplicator 23 Окт 2018 в 21:13
52957514