由于类切片而导致未处理的异常
我在下面的注释行上收到未处理的异常读取位置 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;
}
Main循环序列:
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
运行它抛出错误。双重转换为 dynamic_cast
也会失败并出现相同的错误。
编辑:
经过一些调整,这个变体工作得很好:
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 */;
}
}
I'm getting an unhandled exception reading location 0x00000008 (reading NULL value) on the noted line below, relevant methods leading up to the error are included (continued below examples):
Event Methods:
Event::Event(Event::EVENTTYPE type) : eventType(type) { }
KeyEvent Methods:
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 Methods:
KeyboardKeyDownEvent::KeyboardKeyDownEvent(int scancode) : KeyboardKeyEvent(scancode, Event::KEYBOARD_KEYDOWN) { }
Event Handler Methods:
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;
}
Main Loop Sequence:
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;
}
I know this is a slicing problem I just don't see why it's happening or how to fix it (Actually, now that I think about it, it's probably a "Can not convert to requested type" error). All throughout when I step through the program _scancode
is the appropriate value, but the second the line dynamic_cast<KeyboardKeyDownEvent*>(nxtEvent)->GetKey()
runs it throws the error. Double casting as dynamic_cast<KeyboardKeyDownEvent*>(dynamic_cast<KeyboardKeyEvent*>(nxtEvent))->GetKey()
fails with the same error as well.
EDIT:
After some tweaking, this variant works perfectly:
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 */;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
避免切片的一种解决方案是将基类的析构函数设为虚拟,因此在您的情况下,您可以将
~Event()
设为虚拟:顺便说一句,我想知道为什么要执行以下操作:
为什么你不简单地这样做:
One solution to avoid slicing is to make the destructor of base class virtual, so in your case you can make
~Event()
virtual:By the way, I'm wondering why you do the following:
Why don't you simply do this:
在
Event* EventHandler::DequeueEvent()
中,您有一行Event* result = new Event(*this->_eventQueue.front());
这里发生切片。您可以执行以下操作:
}
然后在派生类中重写
clone()
,例如然后更改
Event* EventHandler::DequeueEvent()
:事件*结果 = (*this->_eventQueue.front()).clone();
In
Event* EventHandler::DequeueEvent()
you have lineEvent* result = new Event(*this->_eventQueue.front());
Here the slicing occurs.You can do the following:
}
Then override
clone()
in derived classes, e.g.Then change
Event* EventHandler::DequeueEvent()
:Event* result = (*this->_eventQueue.front()).clone();
您的 DequeueEvent 方法将始终返回一个 Event 对象,而不是您期望的任何子类。
您的 Dequeue 事件应该返回它正在缓存的实际引用,或者您的基本 Event 类需要提供某种虚拟复制操作来提供真正的克隆。
Your DequeueEvent method will always return an Event object, not any of the sub-classes that you are expecting.
Your Dequeue event should either return the actual reference it is caching, or your base Event class need to provide some sort of virtual copy operation that will provide a real clone.
当您从队列中删除事件时,为什么要复制该事件?这就是进行切片的原因,因为您正在构建基类。相反,将队列上的指针返回给用户。
如上所述,Event 应该有一个虚拟的 ~Event(),以便事件的接收者可以正确删除它。否则,具体类析构函数将无法正常运行。
Why are you copying the Event when you remove it from the queue? That's what is doing the slicing, since you're constructing the base class. Instead, return the pointer that was on the queue to the user.
As noted above, Event should have a virtual ~Event(), so that the recipient of the event can delete it properly. Otherwise, the concrete class destructor will not be properly run.