Channels ▼
RSS

Design

Destructors Considered Harmful


Resource Acquisition Is Initialization

Because freeing memory is dangerous, C++ programmers are taught to allocate memory (and other resources) in constructors and free it in a destructor. This technique is often called RAII (Resource Acquisition is Initialization). For example, instead of writing

	{
           Thing* tp = new Thing;
           // …
           delete tp;
	}

programmers learn to define a class named, say, ThingHandle, and use it this way:

     {
           ThingHandle th;
           // …
     }

The definition of ThingHandle might look like this:

// Do you see what's wrong with this class?
class ThingHandle {
public:
     ThingHandle(): tp(new Thing) { }
     ~ThingHandle() { delete tp; }
     // …
private:
     Thing* tp;
};

The revised code defines a local variable of type ThingHandle, which it creates near the beginning of a block. Doing so, of course, runs the ThingHandle constructor. The local variable is destroyed at the end of the block. Doing so runs the ThingHandle destructor, which frees the memory, and also destroys the pointer to that memory so that the pointer cannot be used again.

In theory, by encapsulating memory allocation in the ThingHandle class, we have ensured that the memory is always freed properly. So, for example, if the code represented by the comment throws an exception, or executes a return statement, the Thing object is still freed.

What's the Harm?

Our ThingHandle class is an abstraction of allocating and freeing memory for a Thing object. Unfortunately, it is an inaccurate abstraction. For example:

     void foo(ThingHandle);
     {
           ThingHandle th;
           foo(th);  // Pass th by value
     }

We have defined foo to accept a ThingHandle object by value. Calling foo, therefore, copies its argument, namely the local variable th. If an object of class type has no copy constructor, copying that object copies its data members.

Therefore, calling foo copies th, which, copies the pointer to the Thing object that th allocated.

So far, so good: The trouble doesn't start until foo returns. When that happens, foo's parameter is destroyed, running the ThingHandle destructor. That destructor frees the memory to which th points. After the code that called foo finishes, the ThingHandle destructor runs again on behalf of th, freeing the same memory a second time. Disaster!

The problem is because ThingHandle has a copy constructor — and, for that matter, a copy-assignment operator — which is part of the class but is not seen. In order for the ThingHandle destructor to be able to free the memory to which tp points, the class has to ensure that no matter how its object was constructed, tp is the last remaining pointer to that Thing object, so that that object is not inappropriately used or freed later. In effect, the nature of the ThingHandle destructor places an obligation on every ThingHandle constructor, even the ones that we do not see directly.

The traditional wisdom is that C++ programmers should avoid such problems by ensuring that every constructor — including the ones that the compiler generates for us — initializes every data member in a way that the destructor can handle. This advice is entirely reasonable. However, programmers usually follow it literally by examining every constructor and making sure that it does the right thing.

I submit that this practice is analogous to — although admittedly simpler than — the practice of examining every label in a program to verify that if it is in fact the target of a goto, the right conditions are present at the time the goto is executed. Yes, it can be done; but often, just as one can do better by avoiding goto statements, it is better to avoid destructors.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video