Avoiding Memory Leaks

From WxWiki

Jump to: navigation, search

Contents

[edit] C++ memory management

(people familiar with C++ may want to scroll down to the wxWidgets-specific part)

[edit] The heap and the stack

Objects are created on the heap or on the stack.

Objects that are on the stack are removed from memory when the current scope terminates (for example when the current function is finished). Objects that are on the heap are deleted when you delete them yourself, with delete or delete[].

So say we have the following class:


 class FooClass
 {
 public:
   FooClass (int n_x, int n_y)
   {
     x=n_x;
     y=n_y;
   }
 private:
   int x,y;
 }


[edit] An object on the stack

If we do the following, the fooObject will be created on the stack, and deleted automatically:

 void foo ()
 {
   FooClass fooObject = FooClass (1, 2);
 }

[edit] An object on the heap

In the following example, we create a pFooObject that, this time, is a pointer to an object on the heap, and thus needs to be deleted. You could also, for example, return a pointer to the object on the heap and delete it elsewhere, but don't forget to do it exactly once! (deleteing the same object twice will cause your program to crash).

 void foo ()
 {
   FooClass * pFooObject = new FooClass (1, 2);
 
   delete pFooObject;
 }

[edit] Anonymous objects go on the stack

If you just temporarily create a FooObject to give to a function as an argument, this is also created on the stack, thus is deleted automatically:

 void bar (FooClass &f) // note the & here, which is a reference symbol (call by reference)
 {
   // Do funky stuff with f
   // f can't be deleted, its not a pointer
 }
 
 void foo ()
 {
   bar (FooClass (1, 2));
 }


As a sidenote, take care with reference symbols (&). If you forget them, things may be more memory intensive than you did expect

 void bar (FooClass f) // note: we forgot the &
 {
   // Do funky stuff with f
   // f can't be deleted, its not a pointer
 }
 
 void foo ()
 {
   bar (FooClass (1, 2)); // note that the FooClass object which you create here is NOT the one 
                          // which bar() will get as a parameter
                          // bar will get a copy of the object (created on the stack)
                          // and when bar() returns, two objects of the class FooClass will be deleted
                          // however, the later may be compiler/optimizer specific
 }


It's generally considered to be a good idea to use constant references when you call functions, which may get huge structures (= classes) as parameters (of course only if the structure is not to be changed). This helps avoiding programming errors and speeding up the code.

 void bar (const HugeFooClass &f)
 {
   // Do funky stuff with f which will not modify f
   // f can't be deleted, its not a pointer
   // f can't be modified, it's declared const
 }
 
 void foo ()
 {
   bar (HugeFooClass (1, 2)); 
 }

[edit] Deleting objects in the heap with wxWidgets

wxWidgets offers two nice macros wxDELETE and wxDELETEA, which you definitly should prefer using when deleting objects:

 /*  delete pointer if it is not NULL and NULL it afterwards */
 /*  (checking that it's !NULL before passing it to delete is just a */
 /*   a question of style, because delete will do it itself anyhow, but it might */
 /*   be considered as an error by some overzealous debugging implementations of */
 /*   the library, so we do it ourselves) */
 #define wxDELETE(p)      if ( (p) != NULL ) { delete p; p = NULL; }
 
 /*  delete an array and NULL it (see comments above) */
 #define wxDELETEA(p)     if ( (p) ) { delete [] (p); p = NULL; }

[edit] Using smart pointers

A less error-prone way of avoiding memory leaks is to use smart pointers, like the ones provided by the Boost library [1].

[edit] The wxWidgets-specific part

If we're talking about a class that is derived off wxWindow, there are some special issues: a wxWindow may get events, and if there's an event that should go to an object that has since been deleted, that may make your program crash. This is why a wxWindow (or wxWindow-derived class) should be deleted with care, first making sure there are no pending events bound for that object.

This is done with the Destroy()-method of wxWindow. Instead of using delete, you must use myWindow->Destroy(), which will wait until next idle loop and then do something like delete myWindow;.

For the same reason, you may never create a wxWindow-derived class on the stack (!), since that will be deleted automatically without using Destroy(). Thus for wxWindow-derived classes, you must always use new and ->Destroy().

[edit] child windows

When a wxWindow is destroyed, it automatically deletes all its children. These children are all the objects that received the window as the parent-argument in their constructors.

As a consequence, if you're creating a derived class that contains child windows, you should use a pointer to the child windows instead of the objects themself as members of the main window.

That is, do

 class myNotebook: public wxNotebook           
 {
  private:
   wxPanel * panel1;
   wxPanel * panel2;
 }

instead of

 class myNotebook: public wxNotebook
 {
  private:
   wxPanel panel1;
   wxPanel panel2;
 }

The second format would cause double-deletion problems.

[edit] "Managed" windows

For "managed" windows, like frames and dialogs, it is usually better to call ->Close() instead of ->Destroy(). This will generate an EVT_CLOSE before destroying the object. If for some reason you want to delete a control yourself, you would use ->Destroy(), since controls are non-managed windows and do not respond to the EVT_CLOSE;.

Note: When calling ->Close(), memory is not actually freed, but the dialog is just hidden from the user. If you really want to free the memory, you will need to call ->Destroy(). If the dialog has been given a parent frame, you may just call ->Close() as the dialog will be destroyed and freed when the parent frame is.

[edit] wxSizers

There was an error in the 2.4.0 documentation: it said that Remove() did not destroy windows. While this is technically true, it neglects to mention that Remove()ing a sizer does destroy the sizer. See also the wxSizer docs for 2.6.3: http://wxwidgets.org/manuals/2.6.3/wx_wxsizer.html#wxsizerremove

[edit] Deleting arrays

If you decide to allocate a C++ array of objects (such as wxBitmap) that may be cleaned up by wxWidgets, make sure you delete the array explicitly before wxWidgets has a chance to do so on exit, since calling delete on array members will cause memory problems.

[edit] Classes that are not derived from wxWindow or wxSizer

Classes that are not derived off wxWindow or wxSizer can be created and deleted like normal C++ classes, as described above, either on the stack or on the heap.

[edit] Careful deleting

Beware of deleting objects such as a wxPen or wxBitmap if they are still in use. Windows is particularly sensitive to this: so make sure you make calls like wxDC::SetPen(wxNullPen) or wxDC::SelectObject(wxNullBitmap) before deleting a drawing object that may be in use. Code that doesn't do this will probably work fine on some platforms, and then fail under Windows.

[edit] See also

http://www.wxwidgets.org/faqcmn.htm#windelete

Personal tools