Tips on memory management with C++ and Qt
Mark Kretschmann
kretschmann at kde.org
Mon Feb 16 08:40:39 CET 2009
Hey all,
as you may have noticed, I've lately been focusing a lot on fixing
critical bugs in Amarok. During this process it occurred to me that
the vast majority of crashes and major stability issues are comprised
of memory management errors.
Why is that? Well, there are a few obvious reasons, e.g. memory
management in C++ is not fully automatic (like in most "modern"
programming languages), but rather manual or semi-automatic (with the
help of libraries). Many of our new contributors come from a Java (or
similar) background, and are used to automatic garbage collection. So
it's a hurdle for them to get memory management right (it can be a
little complex in the beginning). So I decided to start a HOWTO like
mail, collecting a few tips from my experiences, and maybe also
starting a discussion to see what other ideas you guys have for making
Amarok more robust in this regard.
One warning first: I'm in no way an expert authority on this field.
I'm simply a somewhat experienced C++ programmer. That means that this
document may contain mistakes, and if you find them, please let us
know!
So let's start with a few tips:
1)
Use of "Smart Pointers":
A smart pointer in C++ means (in its most simple incarnation) a
pointer that automatically sets itself to 0, when the object it points
to is being destroyed (deleted).
Advantages:
No risk of "dangling pointers". A dangling pointer is a pointer that
has been deleted, but still contains the memory address of the object
that has been destroyed. What happens if you delete this again is
called a "double-free", and almost always leads to a crash. With a
smart pointer, you can delete the object safely again, because
deleting a 0-pointer is defined as a safe (doing nothing) operation in
the C++ standard.
Example:
WRONG:
QWidget* foo = new QWidget();
delete foo;
delete foo;
<BOOOOM>
RIGHT:
QPointer<QWidget> foo = new QWidget();
delete foo;
delete foo;
<not nice, but no crash>
2)
Always make sure not to dereference a 0-pointer:
This is _the_ single most common crash cause in Amarok 2 currently.
It's easy to prevent, but unfortunately also easy to miss:
Example:
WRONG:
Meta::TrackPtr foo;
debug() << foo->prettyUrl();
<BOOOOM>
RIGHT:
Meta::TrackPtr foo;
if( foo )
debug() << foo->prettyUrl();
<no output, and no crash>
3)
Never, ever, use private d-pointer classes in QObject derived subclasses:
What can happen is that you do a "delete d;" in your destructor, and
then Qt goes ahead and auto-deletes other QObject pointers contained
in the private class again, through means of its automatic deleting of
QObjects with a parent Object. -> <BOOOOM>
Read more about this topic in Michael Pyne's interesting blog article:
http://www.purinchu.net/wp/2009/02/04/another-programming-tidbit/
4)
Use Valgrind:
This is one of the most advanced memory debugging tools available,
it's free, and we even have found volunteers that run regular Valgrind
checks (both for memory access bugs and memory leaks) on Amarok trunk.
Reading the Valgrind logs correctly is a bit of an art in itself, but
I'm willing to explain this in another posting, if there is a demand.
Recommended reading on the topic of memory management is this page of
the excellent "C++ FAQ Lite":
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html
So, this wasn't very many tips, but I think I've covered a few
important topics that could already help us a lot. Please feel free to
add your own ideas, and discuss :)
--
Mark Kretschmann
Amarok Developer
www.kde.org - amarok.kde.org
More information about the Amarok-devel
mailing list