0

我在下面的注释行中收到未处理的异常读取位置 0x00000008(读取 NULL 值),包括导致错误的相关方法(以下示例继续):

活动方法:

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

键事件方法:

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()失败也有同样的错误。

编辑:

经过一些调整,这个变体完美地工作:

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

4 回答 4

1

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();

于 2011-07-28T17:41:20.460 回答
1

您的 DequeueEvent 方法将始终返回一个 Event 对象,而不是您期望的任何子类。

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

您的 Dequeue 事件应该返回它正在缓存的实际引用,或者您的基 Event 类需要提供某种虚拟复制操作来提供真正的克隆。

于 2011-07-28T17:41:57.860 回答
1

为什么从队列中删除事件时要复制它?这就是切片的原因,因为您正在构建基类。相反,将队列中的指针返回给用户。

如上所述,Event 应该有一个虚拟的~Event(),以便事件的接收者可以正确地删除它。否则,具体的类析构函数将无法正常运行。

Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = this->_eventQueue.front();
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}
于 2011-07-28T17:43:26.093 回答
1

避免切片的一种解决方案是使基类的析构函数成为虚拟的,因此在您的情况下,您可以使~Event()虚拟:

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;
}
于 2011-07-28T17:38:30.003 回答