Как избежать нарезки объектов в векторе & lt; shared_ptr & lt; Base & gt; & gt;

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

Решение

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

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

Другие решения

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

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

0