Boost logo

Boost :

From: Greg Colvin (gcolvin_at_[hidden])
Date: 1999-07-18 15:00:19


I've been traveling away from my email, so I may have missed
something important, but here are few observations.

The whole point of shared_ptr is sharing, so the current
implementation is optimized for efficient parameter passing
and copying, rather than efficient initial construction.

The canonical example of shared_ptr initialization is:

    shared_ptr<T> p(new T);

In this case it makes very little difference who throws
the exception: operator new(size_t), T(), or shared_ptr<T>().
The expression fails regradless, and there is no memory leak.

Of course you can write code that breaks up this expression:

   void* pv = operator new(sizeof T);
   T* pt = new(pv) T;
   shared_ptr<T> p(pt);

Now each line can throw, for three different reasons, and
in principle you could write code that handles each exception
differently:

   try {
      void* pv = operator new(sizeof T);
      try {
         T* pt = new(pv) T;
         try {
            shared_ptr<T> p(pt);
         } catch {
            // ...
      } catch {
         // ...
   } catch {
      // ...
   }

However, the current shared_ptr will destroy *pt and delete pt
if its constructor throws, making recovery harder.

If I undertand him correctly, Kevin suggests a change which would
make recovery easier, by making it so that pt remains valid if the
shared_ptr constructor throws.

One question is: Do we care enough about ease of recovery in this
case to sacrifice fast copying?

Another question is: How do you avoid a leak in the canonical
example?

    shared_ptr<T> p(new T);

If the shared_ptr constructor throws there is no way to delete
the new T. So it seems you would be forced to write:

   T* pt = new T;
   try {
      shared_ptr<T> p(pt);
   } catch {
      delete pt;
      throw;
   }

Or, since the point of Kevin's examples is to be able to use pt
regardless:

   T* pt = new T;
   try {
      shared_ptr<T> p(pt);
   } catch {
      // do important stuff with pt
      // ...
      delete pt;
      throw;
   }
   // do (the same?) important stuff with pt
   // ...
   // also do stuff with p
   // ...

If you really need such logic, it seems you are better off with:

   T* pt = new T;
   // do important stuff with pt
   // ...
   shared_ptr<T> p(pt);
   // also do stuff with p, unless a throw prevents
   // ...

Which works with the current shared_ptr semantics.

I'll also point out that in most modern environments running out
of virtual memory is rare, and typically causes operating system
performance to degrade horribly, so I see little point in splitting
hairs over which call to new runs out of memory first.

------------------------------------------------------------------------

eGroups.com home: http://www.egroups.com/group/boost
http://www.egroups.com - Simplifying group communications


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk