Необработанное исключение из-за нарезки классов - PullRequest
0 голосов
/ 28 июля 2011

Я получаю необработанное место чтения исключений 0x00000008 (чтение значения NULL) в отмеченной строке ниже, включены соответствующие методы, приводящие к ошибке (продолжение ниже примеров):

Методы события:

Event::Event(Event::EVENTTYPE type) : eventType(type) { }

Методы KeyEvent:

class KeyboardKeyEvent : public Event {
public:
    //...
    int GetKey() const;
protected:
//...
};

int KeyboardKeyEvent::GetKey() const {
    return this->_scancode; //Errors out here. "this" returns 0x000000
}
KeyboardKeyEvent::KeyboardKeyEvent(int key, Event::EVENTTYPE type) : Event(type), _scancode(key) { }

KeyDownEvent Методы:

KeyboardKeyDownEvent::KeyboardKeyDownEvent(int scancode) : KeyboardKeyEvent(scancode, Event::KEYBOARD_KEYDOWN) { }

Методы обработчика событий:

bool EventHandler::EnqueueEvent(Event* event) {
    if(event == NULL) return false;
    try {
        this->_eventQueue.push(event);
    } catch (...) {
        return false;
    }
    return true;
}

Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = new Event(*this->_eventQueue.front());
        delete this->_eventQueue.front();
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}

Последовательность основного цикла:

if(_eh->HasEvents()) {
    Event* nxtEvent = _eh->DequeueEvent();
    switch(nxtEvent->GetType()) {
        case Event::KEYBOARD_KEYDOWN:
            allegro_message("You pressed the %d key!", dynamic_cast<KeyboardKeyDownEvent*>(nxtEvent)->GetKey());
            break;
        default:
            /* DO NOTHING */;
    }
    delete nxtEvent;
    nxtEvent = NULL;
}

Я знаю, что это проблема нарезки, я просто не понимаю, почему это происходит или как это исправить (на самом деле, теперь, когда я думаю об этом, это, вероятно, ошибка «Не удается преобразовать в запрошенный тип»). Повсюду, когда я прохожу программу, _scancode является подходящим значением, но в секунду, когда строка dynamic_cast<KeyboardKeyDownEvent*>(nxtEvent)->GetKey() запускается, она выдает ошибку. Двойное приведение как dynamic_cast<KeyboardKeyDownEvent*>(dynamic_cast<KeyboardKeyEvent*>(nxtEvent))->GetKey() завершается неудачно с той же ошибкой.

EDIT:

После некоторой настройки этот вариант отлично работает:

if(_eh->HasEvents()) {
    switch(_eh->PeekEvent()->GetType()) {
    case Event::KEYBOARD_KEYDOWN:
        allegro_message("You pressed the %s key!", scancode_to_name(dynamic_cast<KeyboardKeyDownEvent*>(_eh->PeekEvent())->GetKey()));
        break;
    case Event::MOUSE_BUTTONDOWN:{
        Mouse::BUTTONS btn = dynamic_cast<MouseButtonDownEvent*>(_eh->PeekEvent())->GetButton();
        if(btn == Mouse::BUTTON2) {
            allegro_message("You pressed the %d button!", dynamic_cast<MouseButtonDownEvent*>(_eh->PeekEvent())->GetButton());
        }
                                 }
        break;
        default:
            /* DO NOTHING */;
    }
}

Ответы [ 4 ]

1 голос
/ 28 июля 2011

Почему вы копируете событие, когда удаляете его из очереди? Это то, что делает нарезку, так как вы создаете базовый класс. Вместо этого верните указатель, который был в очереди, пользователю.

Как отмечалось выше, у Event должно быть виртуальное ~ Event (), чтобы получатель события мог его правильно удалить. В противном случае конкретный деструктор класса не будет запущен должным образом.

Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = this->_eventQueue.front();
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}
1 голос
/ 28 июля 2011

В Event* EventHandler::DequeueEvent() у вас есть строка Event* result = new Event(*this->_eventQueue.front()); Здесь происходит нарезка. Вы можете сделать следующее:

class Event {
 public:
 virtual Event* clone() {
  // create a new instance and copy all the fields
}  

}

Затем переопределите clone() в производных классах, например,

class KeyboardKeyEvent :public Event {
 public: 
 ... 
 virtual KeyboardKeyEvent* clone(); // note - it returns different type
}

Затем измените Event* EventHandler::DequeueEvent(): Event* result = (*this->_eventQueue.front()).clone();

1 голос
/ 28 июля 2011

Ваш метод DequeueEvent всегда будет возвращать объект Event, а не любые ожидаемые подклассы.

    Event* result = new Event(*this->_eventQueue.front());

Ваше событие Dequeue должно либо возвращать фактическую ссылку, которую он кэширует, либо вашу базуКласс событий должен обеспечивать какую-то виртуальную операцию копирования, которая обеспечит настоящий клон.

1 голос
/ 28 июля 2011

Одним из способов избежать нарезки является создание виртуального деструктора базового класса, поэтому в вашем случае вы можете сделать ~Event() virtual:

class Event
{
 public:
    //...    
    virtual ~Event() {}
};

Кстати, мне интересно, почему вы делаете следующее:

//YOUR CODE : its causing the problem!
Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = new Event(*this->_eventQueue.front()); // WHY?
        delete this->_eventQueue.front();  //WHY?
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}

Почему бы вам просто не сделать это:

//Use it. Because it should not cause that probem
Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = this->_eventQueue.front();
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}
...