C++ FAQs
Authors: Cline M. Lomow G. Girou M.
Published year: 2005
Pages: 437-439/566
Buy this book on amazon.com >>

FAQ 31.03 What are the most important principles for resource management?

Ownership, responsibility, and focus.

Ownership: Every allocated resource is owned by exactly one resource manager object, which must be a local ( auto ) variable in some scope (or a member object of some local).

Responsibility: The resource manager object is charged with the responsibility of releasing the allocated resource. This is the only place the resource is released.

Focus: The resource manager object does nothing other than manage the individual resource.

A leak is simply a new that lacks a corresponding delete . Either the delete isn't physically in the source code or it is in the source code but is bypassed due to runtime control flow. Both situations are handled by the resource management discipline since the destructor for a local object always runs when control leaves the scope where the local object was created. In other words, the resource management discipline relies on the guarantees provided by the language rather than the good intentions of programmer self-discipline.

This resource management discipline can be applied to the management of all kinds of resources (e.g., files, semaphores, memory, database connections, and so on). "Memory" is used here only as a concrete example of a manageable resource.

FAQ 31.04 Should the object that manages a resource also perform operations that may throw exceptions?

Not usually.

In the following example, class Fred both manages a resource (an X allocation) and performs some operations that may throw exceptions (it calls the function mayThrow() ). In other words, Fred violates the guideline. When Fred 's constructor throws an exception (as a result of calling mayThrow() ), there is a resource leak.

#include <new>
#include <iostream>
using namespace std;

class X { };

void mayThrow() throw(int)
  { throw 42; }

class Fred {
public:
  Fred() throw(bad_alloc, int);
 ~Fred() throw();
  Fred(const Fred& f) throw();
  Fred& operator= (const Fred& f) throw();
private:
  X* p_;
};

Fred::Fred() throw(bad_alloc, int)
  : p_(new X()) { mayThrow(); }
Fred::~Fred() throw()
  { cout << "Not reached #1\n"; delete p_; }

int main()
{
  try {
    Fred f;
    cout << "Not reached #2\n";
  }
  catch (int e) {
    cout << "Exception caught: " << e << "\n";
  }
}

Because the guideline is violated, the X object leaks: the delete p_ instruction in Fred::~Fred() is never executed. Either Fred should focus on being a resource manager—and nothing but a resource manager—or Fred should delegate the resource management responsibility to some other class. In other words, either get rid of the code that calls mayThrow() from Fred , or change the X* to an auto_ptr<X> .

In those cases where it is not possible to abide by the discipline, a try block can be put in the constructor initialization list. Use this only as a last resort.

FAQ 31.05 Should an object manage two or more resources?

graphics/new_icon.gif

Not usually.

An object that manages a resource should manage exactly one resource. Use composition to combine multiple "pure" resource manager objects (for example, multiple auto_ptr<T> objects, File objects, and so forth). This guideline is a corollary of the guideline presented in the previous FAQ.

If an object manages two or more resources, the first resource may leak if the second allocation throws an exception. In particular, when an exception occurs during the execution of a constructor, the object's destructor is not executed so the destructor won't release the resource that was successfully allocated. In the following example, class Fred manages two resources; an X allocation and a Y allocation. When Fred 's constructor throws an exception as a result of allocating a Y , the X resource leaks.

#include <new>
#include <iostream>
using namespace std;

class X { };

class Y {
public:
  Y() throw(int);
};

Y::Y() throw(int)
  { throw 42; }

class Fred {
public:
  Fred() throw(bad_alloc, int);
 ~Fred() throw();
protected:
  X* x_;
  Y* y_;
};

Fred::Fred() throw(bad_alloc, int)
  : x_(new X())
  , y_(new Y())
  { }
Fred::~Fred() throw()
  { cout << "Not reached #1\n"; delete y_; delete x_; }

int main()
{
  try {
    Fred f;
    cout << "Not reached #2\n";
  }
  catch (int e) {
    cout << "Exception caught: " << e << "\n";
  }
}

Because the guideline is violated, the X object leaks: the delete x_ instruction is never executed. Either Fred should focus on being a manager of a resource—not two or more resources—or Fred should delegate the resource management responsibility to some other class. In other words, either get rid of the Y resource from Fred or change the X* to an auto_ptr<X> .

In those cases where it is not possible to abide by the discipline, a try block can be put in the constructor initialization list. Use this only as a last resort.

C++ FAQs
Authors: Cline M. Lomow G. Girou M.
Published year: 2005
Pages: 437-439/566
Buy this book on amazon.com >>

Similar books on Amazon