Tips on memory management with C++ and Qt

Alex Merry kde at randomguy3.me.uk
Mon Feb 16 22:36:43 CET 2009


On Monday 16 February 2009 10:59:32 Maximilian Kossick wrote:
> On Mon, Feb 16, 2009 at 8:40 AM, Mark Kretschmann <kretschmann at kde.org> 
wrote:
> > 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).

But beware that they don't stop you derefencing a null-pointer.

> >
> >
> > 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/
>
> This is not correct. Using d-pointers in QObjects is perfectly fine as
> long as the destructors are written correctly. The referenced article
> only says that one has to be careful about deleting a private
> d-pointer when other classes keep pointers to members of the private
> class around.

For Amarok, it's a very good rule.  As far as I'm aware, there are no parts of 
Amarok that have binary compatibility guarantees, and so there are no parts of 
Amarok that have any use for d-pointers.

Since a common cause of memory leaks is people forgetting to delete d-
pointers, I would suggest not ever using d-pointers in Amarok.  The reason for 
their existance in kdelibs, for example, is that it helps maintain binary 
compatibility.  If you don't need to maintain binary compatility, don't use d-
pointers.

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

We need a script to clean up Valgrind logs.  For example, anything that 
references libasound or libxine in the backtrace can be discarded, since 
there's nothing we can do about such things (and they appear to make up the 
majority of the multiple-megabyte logs).

I will try to do this at some point, but if someone beats me to it, I won't 
complain.


5) Always initialize member variables.  Or, indeed, variables generally.

Actually, this is not so hard-and-fast.  Inline objects (as opposed to 
pointers-to-objects) don't need explicitly initialising if you're happy with 
their default value (and they have default constructors).  For example

QString m_foo;

doesn't necessarily need any initialization logic, since the default 
constructor will be called by the compiler, giving you an empty string.  But

QWidget* m_bar;

_should_ be set to 0 or set to point to a valid object as early as possible in 
the constructor.  This way you can safely free the object, and you can check 
for whether the pointer is 0 later.

Similarly for simple types.  Booleans and numeric types should be set to false 
and 0 respectively, or another sensible value.  At some point they will be 
used in a test or assignment or as an argument, and if they haven't been 
initialized, then it's anybody's guess what will happen.  And unpredictable 
bugs are the worst ones to debug.

class Foo
{
	QString m_bar; // doesn't need explicitly initialising
	bool m_test;
	int m_number;
	float m_float;
	QWidget* m_widget;
	QWidget* m_anotherWidget;

	Foo( float input )
		: m_test( false ),
		  m_number( 0 ),
		  m_float( input ),
		  m_widget( 0 ),
		  m_anotherWidget( new QWidget )
	{
	}
}

Note that the above is my preferred style of member variable initialization.  
Putting assignments in the constructor body itself is perfectly valid, but by 
the time the constructor has finished, _every_ member variable should be 
initialized (whether implicitly by the compiler, in the case of inline 
objects, or explicitly, in the case of everything else).


Alex


-- 
Why have I got six monitors?  Because I haven't got room for eight.
  -- Terry Pratchett

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: This is a digitally signed message part.
Url : http://mail.kde.org/pipermail/amarok-devel/attachments/20090216/b8dfbfba/attachment.sig 


More information about the Amarok-devel mailing list