Как избежать нарезки объектов в векторе - PullRequest
0 голосов
/ 23 октября 2018

Я храню состояния моей игры (по сути, коллекции сущностей) в векторе общих указателей.При добавлении состояний в вектор, производная часть состояний теряется, и они возвращаются в базовый класс состояний.Все отлично компилируется, но когда я запрашиваю имена состояний, они возвращаются как 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);
    }

    // ...

}

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

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

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.

0 голосов
/ 24 октября 2018

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

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

...