Re: Good style for memory management?



On 3/18/07, Paul Davis <paul linuxaudiosystems com> wrote:
On Sun, 2007-03-18 at 17:50 +0100, Roland Schwarz wrote:

> that if finding an error in the file throws out. Since you are not the
> author of the parser you either have to
> *) wrap everything in try {} catch() {} blocks or use
> *) resource acquisition is initialization idiom.

most good books on C++ that i have read which tackle this issue
recommend avoiding throwing out of constructors anyway. i think meyers
and others recommend constructing an object as minimally as possible and
then explicitly "initializing" it. this is in part precisely to deal
with the kind of ambiguity that you are encountering. thus, if you have

  class MyFancyWidget : public class Gtk::SomeWidget {.... }

you construct such a thing like this:

     MyFancyWidget* w = manage (new MyFancyWidget()); // does almost
nothing

     try {
         w->init (some, args, it, might, need);
     }

     catch (....

since this allows you to handle exceptions cleanly and clearly.


_______________________________________________
gtkmm-list mailing list
gtkmm-list gnome org
http://mail.gnome.org/mailman/listinfo/gtkmm-list


Originaly I had this at the end, but the email got kind of long, so
here's the point I'm actually making.  The rest is just more
theoretical stuff.

"Use glade religiously, smart pointers for top level windows, and you
should almost never have to worry about memory management."

This could get a bit complicated with a third Paul entering the thread....

There seem to be three main points that I'd like to add my two cents on.

First, this suedo code:

void
foo()
{
    Gtk::Button* b = Gtk::manage( new Gtk::Button( "Test") ) ;
}

Is leaking the button widget.  If I'm not mistaken 'manage' explicitly
means 'delete widget when removed from parent container'.  Thus, it
will never be deleted if its never added to a container.

This I believe also makes the original code snippets posted invalid.
Placing the widget pointers in the auto_ptr *and* having them managed
will eventually lead to double deletion.  Which is bad.

I'd say 99% of all widgets I've ever created are managed.  The last 1%
are the top level windows which I put in boost::shared_ptr's  There's
always contradictory cases, but that should help you most of the time.

I also recommend using libglademm which would save you quite a bit of
effort in the first place.

The discussion about throwing errors when building your gui is right
and wrong in my opinion.  A general rule of thumb, is only do things
that could lead to an exception being thrown when you could unwind the
stack without leaking memory. Which is pretty obviously a 'no duh'
sort of statement.  But specifically, never throw an exception when a
widget has been managed and not added to a container. Then as one of
the other Paul's said, if your GUI is throwing exceptions, its not
time to worry about leaking memory, its time to fix the error in your
program.

The last thing that the other Paul Davis brought up is the
ctor()/init() method of object creation.  This subject comes up on
mailing lists and generally never gets resolved either way that I've
ever seen.

Personally, I think the ctor()/init() pair is flat out wrong.  But its
a personal choice, there's no real right or wrong in this case.

As far as I can tell the main reasoning behind this pair of methods is
that some think its bad to throw an exception from a constructor. This
is wrong. I tend to believe that people are inferring this from the
fact that you *shouldn't* throw an exception from a *destructor*. ( I
still haven't bought myself a copy of the C++ standard so I'm not
certain if this is standards required, or just good practice. But when
I say good practice I mean it, if you throw from a destructor as the
stack is unwinding for a different exception your program goes boom
and halts immediately as the second exception is thrown. Or at least
it should. If it doesn't then you've walked into the land of undefined
behavior )

The only tricky part about throwing exceptions from a constructor, is
that, when a constructor throws an exception, the destructor isn't
called. Some people point at this as grounds to never throw from a
constructor as well. The reckon if the destructor is never called,
then how could resources acquired in the constructor be freed?  So the
idea is make construction a two step process.  This way the destructor
will always be called and always free resouces. I believe this is the
wrong answer.

The right answer is 'Never ever use objects that don't abide by RAII'.
(And yes, when building the base RAII classes, make sure they don't
throw themselves.) The point being, even though the class's destructor
isn't called when the constructor throws, each of its member
variables' destructor is called. So if we abide by RAII for all
classes then throwing from the constructor is fine, and having the
ctor()/init() pair is no longer needed.

There is another thing that I always feel should be pointed out about
using the ctor()/init() pair. Generally, all objects have a lifetime
from the call to the constructor to the destructor.  And its a single
state lifetime.  Ie, its just alive.  Using the ctor()/init()
introduces a second state.  you have pre-init() and post-init().   You
can no longer reasonably make the assumption that an object has been
initialized. Which means you have to check for it.  Everywhere. For me
its a headache I can avoid, so I do.

Plust I just find this annoying to look at:

MyObject* b = new MyObject() ;
b.init( "Hello, World!" ) ;

Anyway, thats all I've really got on the subject.

Paul Davis



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]