C++: в списке базовых объектов производная память дает неожиданные результаты при передаче по ссылке
У меня возникают некоторые проблемы с пониманием границ использования ссылок вместо указателей: как я могу передать производный объект без виртуальных функций, только набор данных, в список базовых объектов?
В моем приложении есть объект события и очередь:
class Event {
String name;
public:
Event(const String &name_);
};
class KeyEvent : public Event {
public:
int key;
};
std::queue<Event> eventsQueue;
Когда я преобразую объекты из очереди в их исходные типы, я получаю неопределенные значения во всех переменных, чего никогда не было с указателями. В общем,
while(!eventsQueue.empty()) {
auto &e = eventsQueue.front();
if(e.name == "keyPress") {
std::cout << static_cast<KeyEvent &>(e).key;
}
}
дает мне бред: http://ideone.com/rbzGJV
Есть ли способ исправить это без использования указателей? Есть ли способ иметь ссылку внутри контейнера, как std::queue
?
2 ответа
Событие - это блок памяти с определенным размером, который является размером объекта String. KeyEvent - это блок памяти с размером sizeof(Event) + sizeof(int), который на 4 байта (вероятно) больше. Вы храните вещи в контейнере типов Event, поэтому каждый элемент в контейнере имеет размер Event, и int... ну... он упадет с конца в неопределенную область поведения. За счет хранения указателей вместо этого каждый элемент в контейнере будет иметь размер типа указателя, независимо от того, на что он указывает.
Я понятия не имею, что вы сделали, чтобы объединить эти объекты KeyEvent в контейнер Event, но что бы это ни было, я рекомендую больше этого не делать!
PS Ваша фраза предполагает Java-фон? В C++ "Событие е;" это не ссылка, это событие. Событие & e; это ссылка на событие. Событие * е; указатель на событие; Event & e реализована внутри как указатель в большинстве компиляторов C++, но вам не нужно об этом беспокоиться. Я е; думаю, что в поведении ближе всего к тому, что сделал бы Java, если бы вы сказали Event e;
является std::shared_ptr<Event>
Ваша очередь содержит объекты Event, поэтому, когда вы нажимаете KeyEvent, это неявное приведение к Event, а затем объект Event создается в очереди путем копирования. Затем вы приводите объект Key к событию KeyEvent, что означает, что вы начинаете читать память для ключевого поля извне объекта, отсюда и мусор.
Если вы хотите иметь полиморфные типы, вы должны использовать указатели.