Avoiding Memory Leaks

From WxWiki
Revision as of 19:36, 19 October 2018 by Tierra (talk | contribs) (Text replacement - "<source>" to "<syntaxhighlight lang="cpp">")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

For an introduction to memory leaks you may want to read the relative Wikipedia page. What follows here are some hints for avoiding them when using wxWidgets in a C++ program. The contents of this page should apply to all versions of wxWidgets.

C++ memory management

In this section some basilar concepts are described. People familiar with C++ may want to scroll down to the wxWidgets-specific part.

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

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

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

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 const &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)); 
  }

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

Using smart pointers

A less error-prone way of avoiding memory leaks is to use smart pointers, like std::auto_ptr (replaced by std::unique_ptr) or the ones provided by the Boost library [1].

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(). There is however one exception to this, see #Modal windows below.

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.

"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.

Modal dialogs

Modal dialogs (i.e. all dialogs that are shown with the ShowModal() method) have their own event queue and thus, could be deleted without using the delayed deletion mechanism (i.e. Destroy() function). For those dialogs, it is thus safe to directly use the delete operator:

wxMessageDialog* dlg = new wxMessageDialog(this, _T("Example"), _T("Example"), wxOK | wxICON_INFORMATION);
dlg->ShowModal();
delete dlg;

Since they can be deleted without caring about pending events, those dialogs can be safely instanciated on the stack. But, in this case, you should not invoke Destroy() method since this would lead in a double deletion of the dialog (which would lead to a crash of the application).

wxMessageDialog dlg(this, _T("Example"), _T("Example"), wxOK | wxICON_INFORMATION);
dlg.ShowModal();

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.

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.

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.

Automatic leak detection with Microsoft VisualC++

An handy feature provided by MSVC are the CRT debug functions which among other things allow you to easily spot memory leaks in your program. wxWidgets makes it very easy to use them. You just need to add at the beginning of all your source files the following include:

#ifdef __WXMSW__
    #include <wx/msw/msvcrt.h>      // redefines the new() operator 
#endif

The only gotcha are:

  1. you should include it only after including all other STL headers otherwise you could get tons of compilation errors (e.g. some STL headers with MSVC 2008 include the 'xdebug' header which manipulates the 'new' operator, too, resulting in various problems)
  2. you should not use the malloc(), calloc() & co. functions to allocate memory or, if you really need to use them, then you have to define the '_CRTDBG_MAP_ALLOC' symbol before the MSVC crtdbg.h header is included; this may not be so easy given that there are some STL headers (e.g. 'string') which also include that file. An easy way to make sure _CRTDBG_MAP_ALLOC is surely defined before crtdbg.h is included is to define the symbol in your project file or your makefile (/D_CRTDBG_MAP_ALLOC) rather than defining it in your code.

To test if the leak detection mechanism works fine, you can simply add somewhere a "new char[100];" instruction and then run the program and see if at its exit that artificial memory leak is detected. Another possible test is to place the following lines after including the msvcrt.h header:

#if !defined(_INC_CRTDBG) || !defined(_CRTDBG_MAP_ALLOC)
    #error Debug CRT functions have not been included!
#endif

Last, it may be useful to know that the _CrtSetDbgFlag() call (for docs about it see http://msdn.microsoft.com/en-us/library/5at7yxcs%28VS.71%29.aspx) which dumps the memory leaks at the end of your program is placed in the wxWidgets/src/common/init.cpp file inside a static ctor and thus is automatically called at program startup.

Automatic leak detection with valgrind

A command-line-tool that allows for leak detection and that is available for free is valgrind: Just start your program as

valgrind --leak-check=full <your_program> [params]

instead of

<your_program> [params]

and detailed information about memory leaks (including the line number of the new() operator and the call stack leading to it) will be output to the console. By default valgrind does lots of additional tests for things like the use of uninitialized variables or use-after-free-conditions which makes the program run considerably slower but creates additional helpful output.

See also

https://www.wxwidgets.org/docs/faq/common/#windelete