Я храню состояния моей игры (по сути, коллекции сущностей) в векторе общих указателей. При добавлении состояний в вектор, производная часть состояний теряется, и они возвращаются в базовый класс состояний. Все это прекрасно компилируется, но когда я запрашиваю имена состояний, они все возвращаются как 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);
}
// ...
}
Для доступа к члену методы производного класса через указатель (или ссылку) на его базовый класс, вы должен использовать полиморфизм (что вы не сделали). Например
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
,
name
в State
а также name
в Overworld
две полностью независимые переменные класса. Они не являются частью какого-либо состояния экземпляров, и вы не можете напрямую запросить экземпляр для переменных класса, так как они не могут быть virtual
, Чтобы получить доступ к переменным класса полиморфно, вам нужно использовать виртуальную функцию.
Добавить такую функцию-член в State
и не забудьте переопределить его в производных классах по мере необходимости. Или, вы знаете, вы можете просто использовать языки стандарта RTTI, используя typeid
.