由于类切片而导致未处理的异常

发布于 2024-11-27 02:43:00 字数 2842 浏览 1 评论 0原文

我在下面的注释行上收到未处理的异常读取位置 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(nxtEvent)->GetKey() 运行它抛出错误。双重转换为 dynamic_cast(dynamic_cast(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 */;
    }
}

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

过去的过去 2024-12-04 02:43:00

避免切片的一种解决方案是将基类的析构函数设为虚拟,因此在您的情况下,您可以将 ~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;
}

One solution to avoid slicing is to make the destructor of base class virtual, so in your case you can make ~Event() virtual:

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

By the way, I'm wondering why you do the following:

//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;
}

Why don't you simply do this:

//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;
}
合久必婚 2024-12-04 02:43:00

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()
事件*结果 = (*this->_eventQueue.front()).clone();

In Event* EventHandler::DequeueEvent() you have line
Event* result = new Event(*this->_eventQueue.front()); Here the slicing occurs.
You can do the following:

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

}

Then override clone() in derived classes, e.g.

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

Then change Event* EventHandler::DequeueEvent() :
Event* result = (*this->_eventQueue.front()).clone();

音盲 2024-12-04 02:43:00

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

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

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

Your DequeueEvent method will always return an Event object, not any of the sub-classes that you are expecting.

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

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.

想挽留 2024-12-04 02:43:00

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

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

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

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.

Event* EventHandler::DequeueEvent() {
    if(this->_eventQueue.empty() == false) {
        Event* result = this->_eventQueue.front();
        this->_eventQueue.pop();
        return result;
    }
    return NULL;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文