Here is my issue:
I'm making a game GUI API. It has a mouse listener. Lets say the mouse listener is instructed to delete whatever widget it is listening to. The problem is that, when it deletes it, the iterator is destroyed to so it crashes:
for(std::vector<AguiMouseListener*>::iterator it
= mouseListeners.begin(); it != mouseListeners.end(); ++it)
{
switch (event)
{
case AguiMouseEventArgs::AGUI_MOUSE_DOWN:
(*it)->mouseDownCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_UP:
(*it)->mouseUpCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_MOVE:
(*it)->mouseMoveCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_CLICK:
(*it)->mouseClickCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_DOUBLE_CLICK:
(*it)->mouseDoubleClickCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_WHEEL_UP:
(*it)->mouseWheelUpCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_WHEEL_DOWN:
(*it)->mouseWheelDownCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_ENTER:
(*it)->mouseEnterCallback(mArgs);
开发者_JAVA技巧 break;
case AguiMouseEventArgs::AGUI_MOUSE_LEAVE:
(*it)->mouseLeaveCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_HOVER:
(*it)->mouseHoverCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_DRAG:
(*it)->mouseDragCallback(mArgs);
break;
case AguiMouseEventArgs::AGUI_MOUSE_MODAL_DOWN:
(*it)->mouseModalDownCallback(mArgs);
break;
default:
break;
}
}
Is there a proper way for another object to delete what it is listening to from a callback?
Thanks
class BillButton : public AguiMouseListener {
public:
void mouseLeaveCallback(AguiMouseEventArgs &mouseArgs)
{
delete mouseArgs.getSourceWidget();
}
};
You could use a std::list instead of a std::vector. When you remove items from it, you won't invalidate iterators, so you can keep looping
You could build up a vector/list of objects that are to be deleted once the loop has finished. Once the loop completes, loop through your new collection, delete them, and remove them from the original vector. The new collection could be of type pointer-to-pointer, so you don't destroy it while iterating over it
You could add a return value from your callback that allows for some flow control. An example is how Win32 and WinForms event handlers set a
wasHandled
flag. In your case, you'd tell the handler loop whether you want to terminate (as well as any other flow control that sounds interesting/useful)You could reorganize your code to use reference counting smart pointers to automatically delete it when there are no more listeners (or other things referencing your object).
For the smart pointer solution, one such class is the std::shared_ptr. You can find this in brand-spanking-new compilers (it may not be standard yet, but it will be soon), or in the Boost Smart Pointers library.
I'd suggest returning a boolean from your callbacks, indicating that the callback consumed the event. When true is returned, the loop over callbacks stops. This is useful in other situations besides deleting the callback owner.
Also, be careful not to access the widget state after a callback has been invoked. Make copies of needed state variables if necessary.
You don't want to delete objects that can still be processing events (especially the event that is currently being processed). View hierarchy can get offensively intertwined.
Your handler should not do the delete itself but register another event with the objects parent to do the actual delete, then return normally (you may want to set a field on the object that says ignore other events so it does not do any more processing).
When the parent gets a delete event it deletes the object that is scheduled to delete.
I use the term parent loosely as any object higher in the view hierarchy up to and including the window manager.
精彩评论