#c #list #c 11 #iterator
#c #Список #c 11 #итератор
Вопрос:
У меня ошибка «итератор списка не разыменовывается». Я понял, что это часто вызвано попыткой слишком далеко перебирать список, однако я не могу найти причину этого. Я остаюсь строго в рамках функций std::list
API, поэтому я не знаю, что именно происходит не так в приведенном ниже коде, чтобы вызвать эту ошибку.
Ошибка связана с несколькими загадками. Основное внимание в этой проблеме уделяется GameStateManager::HandleEvents()
во время list
итерации. В момент разрыва я добавил в список пять элементов, вызвал list::remove()
для каждого (создав пустой список) и добавил еще четыре элемента. Однако, как вы можете видеть ниже, я печатаю размер списка перед его повторением. Во время отладки это показало нечто сбивающее с толку — размер, возвращаемый list::size()
, отличается от фактического размера (как показано в самой переменной list::size через отладчик Visual Studio). Это происходит только до ошибки — обычно оба значения одинаковы. Однако прямо перед ошибкой функция size() возвращает значение, неравное значению элемента size списка. Он последовательно содержит предыдущее значение до удаления первых элементов. Я не могу не чувствовать, что это несоответствие как-то связано с ошибкой «итератор списка не разыменовывается».
Кроме того, сбивает с толку то, что команда print не выполняется непосредственно перед кодом, вызывающим ошибку. Кроме того, в GameStateManager::HandleEvents()
, никакие пользовательские точки останова не запускаются до фатального for
цикла. Возможно, эти аномалии являются отдельными проблемами, однако я подумал, что их может быть полезно знать.
GameStateManager.h
#pragma once
#include "Tools.h"
#include "TitleScreenState.h"
#include "Game.h"
#include "Timer.h"
#include <list>
class GameState;
class EventHandler;
class GameStateManager
{
public:
GameStateManager(void);
~GameStateManager(void) { m_currentState->OnEnd(); }
void Run(void);
void Quit(void) { m_running = false; }
// Event Handler Functions
void AddHandler(EventHandler* handler) { m_eventHandlers.emplace_back(handler); }
void RemoveHandler(EventHandler* handler) { m_eventHandlers.remove(handler); }
private:
std::list<EventHandler*> m_eventHandlers; // All of the handlers who are passed the events
bool m_running; // The universal boolean for whether or not the program is running
int delta; // The time since the last frame
Timer m_FPSTimer; // The timer that keeps track of the time since the last update
GameStateID startStateID;
GameState* m_currentState; // Pointer to the current game state
// Poll and pass events to the current state
void HandleEvents(void);
};
GameStateManager.cpp
#include "GameStateManager.h"
#include "EventHandler.h"
#include "ToolKit.h"
#include "Game.h"
#include <time.h>
GameStateManager* g_manager = nullptr;
GameStateManager::GameStateManager(void)
: m_eventHandlers(), delta(0), m_running(true), startStateID(GSID_TITLE),
m_currentState(nullptr), m_titleScreen(nullptr), m_game(nullptr)
{
// Initialise time
srand(time(nullptr));
}
void GameStateManager::Run(void)
{
Initialise();
while (m_running)
{
// State's key press responses
m_currentState->OnKeys(SDL_GetKeyboardState(nullptr));
// Update State
m_currentState->OnUpdate(delta);
// Render State
m_currentState->OnRender();
HandleEvents();
}
delete this;
}
void GameStateManager::HandleEvents(void)
{
// Respond to events
SDL_Event event;
while(SDL_PollEvent(amp;event))
{
if (event.type == SDL_QUIT) {
m_running = false; // Quit
}
else
{
int size = m_eventHandlers.size();
printf("Whooo! Size: %dn", size);
for (EventHandler* handler : m_eventHandlers)
handler->OnEvent(event);
}
}
}
Комментарии:
1. Какой-то обработчик событий отменяет регистрацию в своем onEvent()? Если это так, это приведет к прерыванию вашей итерации.
2. Не могу сказать наверняка, но одна проблема, которую я могу представить, заключается в том, что любой из
handler->OnEvent(event);
вызовов вызываетGameStateManager::RemoveHandler
некоторые другие обработчики. Можетfor
быть, тогда цикл не сможет правильно выполнить итерацию списка?3. В качестве обходного пути с улучшенным документированием сделайте копию
std::list<EventHandler*>
в локальный автоматический список или вектор перед циклом и перечислите этот контейнер вместоm_eventHandlers
. Если обработчик отменяет себя во время перечисления, он сделает это, не влияя на ваше перечисление. это просто список указателей, поэтому его репликация должна быть быстрой.4. Справедливые предложения, но ни один из обработчиков не должен разыменовывать себя. Я попробую метод локального копирования и посмотрю, как это происходит, просто чтобы быть уверенным. В нынешнем виде четыре обработчика представляют собой пункты меню, такие как Возобновить игру, выйти из игры и т.д. Однако выполняются только в том случае, если событие вызывает щелчок мыши — ошибка запускается без предварительного ввода. Тем не менее, стоит попробовать проверить это. До тех пор любые другие предложения будут оценены. Возможно, устранение причины неисполненных триггеров печати и точки останова перед циклом for .
5. Просто установите точку останова данных при изменении размера элемента данных, и вы увидите, когда именно оно неожиданно изменится. Либо ваш список изменяется во время итерации по нему, либо это может быть какая-то проблема с повреждением памяти. В качестве примечания: ваш код не является потокобезопасным: по крайней мере, m_running должен быть атомарным.