The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

RAII, contructor throws, ugliness results

I'm trying to use RAII in C++ and I have the following problem.

When the constructor for CTheClass runs, I want to acquire 3 resources. These 3 resources will be freed in the destructor (that's RAII, right?)

But, each resource acquisition might throw an exception. If the third one throws, then resources 1 and 2 have been acquired.

Since the constructor for CTheClass has not finished running, the destructor will not be called, so
resources 1 and 2 will not be freed.

Constructor:
CTheClass::CTheClass
{
  m_Resource1 = new Resource1; // could throw but doesn't
  m_Resource2 = new Resource2; // could throw but doesn't
  m_Resource3 = new Resource3; // this throws
}

Destructor:
CTheClass::CTheClass
{
  delete m_Resource1;
  delete m_Resource2;
  delete m_Resource3;
}

To work arount this, one possible solution would be:
Constructor:
CTheClass::CTheClass
{
  try
  {
    m_Resource1 = new Resource1; // could throw Exception1
    m_Resource2 = new Resource2; // could throw Exception2
    m_Resource3 = new Resource3; // throws Exception3
  }
  catch( Exception2& e )
  {
    delete m_Resource1;
  }
  catch( Exception3& e )
  {
    delete m_Resource1;
    delete m_Resource2;
  }
}

But this is pretty ugly, when I write each catch block I have to be careful of the order of things and if I ever add another resource it will be a pain.
I thought RAII was promising I wouldn't have to worry about that stuff!

Does anyone have a more elegant solution?
Parisian developer ISO cute geek girl
Sunday, August 28, 2005
 
 
One option is to use smart pointers for the resources. The smart pointers will take care of any allocated resources when they go out of scope.
Jeff Mastry Send private email
Sunday, August 28, 2005
 
 
"One option is to use smart pointers for the resources. The smart pointers will take care of any allocated resources when they go out of scope."

He is trying to build his own smart pointer with three resources instead of one... RAII is the basis for smart pointers.
PPR
Sunday, August 28, 2005
 
 
A simple solution for a exception throwing new is using new(nothrow) .. or new(std::nothrow) and check if the pointer is not null before contiuning.
PPR
Sunday, August 28, 2005
 
 
Well if m_Resource1... were in some sense 'smart', in that their destructors cleaned up whatever the objects were pointing at, then if an exception were thrown they would clean up after themselves. So I think turning them into smart pointers would be one possible solution.

This would have two other advantages. Firstly, CTheClass would need no destructor, as the objects would clean up after themselves automatically. (That's kind of what this whole raii thing is all about.) And secondly, the mechanics of keeping track of a resource would be held in the smart objects' class, rather than being repeated 3 times as in the current situation. (Granted, this is not really a practical issue for new-delete...)
Tom_
Sunday, August 28, 2005
 
 
People sometimes wonder why C++ projects sometimes long time to finish, why C++ projects are bugprone. This whole thread is a good demonstration of why. It is hard to write correct  C++ code. There are a few things in action in this example.

1. new must always be checked for bad_alloc unless nothrow is set.
2. even if nothrow is set, the constructors of the objects that are initialized could also throw other exceptions so they must also be checked
3. Implementing a smart pointer that initializes the objects for you. But the initialization of smart pointers in a constructor requires reference counting. Although there are ready made e.g. Boost.

It would seem that smart pointers is the best solutions but these aren't always correct.

My suggestion is set all pointers before initialization to NULL, catch exceptions and if a exception is thrown delete all pointers. The delete statement should be a NoOp when given a NULL to delete.
Take a guess.
Sunday, August 28, 2005
 
 
Change this:

Constructor:
CTheClass::CTheClass
{
  m_Resource1 = new Resource1; // could throw but doesn't
  m_Resource2 = new Resource2; // could throw but doesn't
  m_Resource3 = new Resource3; // this throws
}

to this:

Constructor:
CTheClass::CTheClass
{
  shared_ptr<ResourceType*> m_Resource1(new ResourceType,
ResourceDeleterFn);
  shared_ptr<ResourceType*> m_Resource2(new ResourceType, ResourceDeleterFn);
  shared_ptr<ResourceType*> m_Resource3(new ResourceType, ResourceDeleterFn);
}

Boost has a shared_ptr implementation.
Tony
Sunday, August 28, 2005
 
 
magic?
PPR
Sunday, August 28, 2005
 
 
Use smart pointers for each m_Resource instead of raw pointers.  Subobjects are constructed before the containing object and thus will be destructed regardless of whether or not the containing object's construction succeeded.
SomeBody Send private email
Sunday, August 28, 2005
 
 
Thanks everyone.

I guess I will have to look into all this: new(nothrow), boost, smart pointers, shared_prt, auto_ptr, etc, etc
I also found this:
http://www.dslextreme.com/users/fabrizioo/rant/en/except.html

Add to this the fact that I just discovered that depending on the compiler, and depending on if you're using MFC or not, apparently new can either throw or return null.
http://msdn.microsoft.com/msdnmag/issues/03/09/LegacySTLFix/default.aspx

Is that really a issue? How do you guys deal with this?

Tsss and I thought C++ was supposed to make my life easier... how naive (I used to do plain C).
Parisian developer ISO cute geek girl
Sunday, August 28, 2005
 
 
I would recommend reading Effective C++ and More Effective C++ - ISBN 0-201-31015-5

The idea behind smart pointers of various types (as I see it) is that the smart pointer itself has nothing of real import to cleanup and it only fails if you are out of memory.

std::auto_ptr works fine for most of the things I've done, I've never had access to Boost.

try
class CTheClass
{
private:
  std::auto_ptr<Resource1> m_Resource1;
  std::auto_ptr<Resource2> m_Resource2;
  std::auto_ptr<Resource3> m_Resource3;
}

CTheClass::CTheClass
: m_Resource1( new Resource1 ) // could throw but doesn't
, m_Resource2( new Resource2 ) // could throw but doesn't
, m_Resource3( new Resource3 ) // this throws
{
}

no need for a destructor, the auto_ptr will keep that clean for you.

Sunday, August 28, 2005
 
 
Using smart pointers for each resource is the way to go.

If you use auto_ptr you should watch out for its behavior during copy, if your class is meant to be copyed, then you should implement operator=() or use a shared_ptr (depending is the resources are shared among copies or not).

If you don't allow copies, you should declare operator=() as a private member. And the copy-constructor also!

The Effective C++ and More Effective C++ references are a good suggestion too.

And, about this being a "weird" or "wrong" behavior in C++, I cannot see what would be the correct behavior, in any language/environment, for this situation. Leave it to the GC? Maybe, depending on the resource.

You would probably need a finally block or the same wrapper class concept anyway...
Whatever
Monday, August 29, 2005
 
 
>> And, about this being a "weird" or "wrong" behavior in C++, I cannot see what would be the correct behavior, in any language/environment, for this situation. Leave it to the GC? Maybe, depending on the resource. <<

I was wondering the same thing.  GC might be fine for memory but what about database connections, network resources, file locks, mutex releases, OS resources, etc.?  I haven't seen anything in other languages that matches deterministic destruction in C++.  C# has 'using' but that's about it.
SomeBody Send private email
Monday, August 29, 2005
 
 
On a side note - C#'s "using" seems pretty nice for local scope trash control. In this case C# code could look like the following:

// Constructor
private CTheClass
{
  using(m_Resource1 = new Resource1())
    using(m_Resource2 = new Resource2())
      using(m_Resource3 = new Resource3());
}

This code will dispose disposables, but will keep all exceptions from aggregated objects unhandled.



I was not doing much of C++, so I'm a little confused, why (smart pointers apart) something like a null check couldn't be used in OP's code:

Constructor:
CTheClass::CTheClass
{
  try
  {
    m_Resource1 = new Resource1();
    m_Resource2 = new Resource2();
    m_Resource3 = new Resource3();
  }
  catch(...)
  {
    // if(m_Resource3) delete m_Resource3;
    if(m_Resource2) delete m_Resource2;
    if(m_Resource1) delete m_Resource1;
  }
}
DK
Monday, August 29, 2005
 
 
> GC might be fine for memory but what about database connections, network resources, file locks, mutex releases, OS resources, etc.?

There's a popular belief these days that memory is the most common resource, therefore the only one that needs to be managed.

Curiously, there's also a popular belief (backed by large RAM sizes in current home computers and virtual memory) that memory is the one resource that's least important to avoid leaking, simply because it doesn't matter.

On Windows, this is not true. There are a great many resources (file handles, GDI objects, sockets, etc) that are in short supply. The logic that developers can just be careful with those resources is quite silly. Even just opening a window and displaying a bitmap requires several resources to be allocated and tracked. It's not hard to do, but if you think of them as merely being memory allocations, then either memory can be managed manually or they should be managed automatically as well. COM requires careful use of the manual reference count as well, and one missed function call can cause a system-wide leak or an obscure crash at a completely unrelated location, possibly in another application. All of this will continuie to be true on Windows Vista and in .NET unless assorted resources can be managed automatically. (In particular, anything backed by the Win32/64 API that uses RAM that can't be swapped out to disk is potentially a very serious leak. If your server leaks sockets, it won't take long to bring down an entire server.)


My take on this version:

CTheClass::CTheClass
  // ensure pointers are set to NULL - this is not guaranteed
  // unless done explicitly. all 3 pointers will be cleared before
  // the constructor body is executed. (remember the RAM
  // occupied by an instance may be anywhere, and is almost
  // certain to contain random data. many compilers clear
  // data to set values in debug build, but often they ensure
  // pointers have invalid non-NULL values, to help
  // ensure a crash if they're used without being initialized)
  : m_Resource1(NULL) , m_Resource1(NULL) , m_Resource1(NULL)
{
  try
  {
    m_Resource1 = new Resource1();
    m_Resource2 = new Resource2();
    m_Resource3 = new Resource3();
  }
  catch(...)
  {
    delete m_Resource3; // <-- deleting a NULL pointer is valid and safe
    delete m_Resource2;
    delete m_Resource1;
    throw; // <-- important! propogate the actual error that occured
    // possibly change this to throw CTheClass::EmyCustomException
    // if callers don't need to know the implemention details
  }
}

A redundant if() protecting a delete causes two code paths for no actual benefit.

It's not entirely a good idea as each resource should have its own smart pointer, but in practise as long as CTheClass is small (always a good idea anyway) it could reduce some overhead.

Monday, August 29, 2005
 
 
Aa far as I can remember C++ does ugly things when throwing exceptions out of constructors and especially destructors. Problem comes from mixing memory allocation/deallocation and exception throwing.

Therefore I would keep any exception throwing code out of constructors and destructors.

class CTheClass {

private:
  CTheClass() {}
  ~CTheClass() {}

  void init() {
    // aquired resources
    // may throw exceptions
  }

  void shutdown() {
    // release resource
    // may throw exceptions
  }
 
public:
  static CTheClass* create() {
      CTheClass* theClass = new CTheClass();
      try {
          theClass->init();
      } catch (...) {
          try {
              theClass->shutdown();
          } catch(...) { ; }
          delete theClass;
          throw; // or theClass = NULL;
      }
      return theClass;
  }

  void delete() {
      try {
          shutdown();
      } catch ( ...
          delete this;
          throw;
      }
      delete this;
  }
}


void main(void) {
  CTheClass* theClass = NULL;
  try {
    theClass = CTheClass::create();
    // use theClass ...
    theClass->delete();
  } catch( ... ) {
    // handle the exception; the resources and memory
    // have been released already.
  }
}
Dino Send private email
Monday, August 29, 2005
 
 
Alternatively use inversion of control pattern:
1) Aquire resources firts
2) Build object based on reference to resources
3) Use
4) AFTER CTheClass object has been released, release the resources too.
Dino Send private email
Monday, August 29, 2005
 
 
Dino wrote "Aa far as I can remember C++ does ugly things when throwing exceptions out of constructors and especially destructors. Problem comes from mixing memory allocation/deallocation and exception throwing. Therefore I would keep any exception throwing code out of constructors and destructors."

Your understanding is incomplete.
1) It's a bad idea to throw an exception in a destructor. See http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3
2) You should use an initialization list in your constructor when possible, which is nearly always - see http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.6
3) If your constructor may fail, you should throw an exception. See http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.2

The example code above by an unnamed poster who used auto_ptr members and the initialization list was correct.

These are not merely my opinions. They are the consensus of the C++ community.
Same person, new name Send private email
Monday, August 29, 2005
 
 
Use a smart pointer; don't throw from a destructor; and read Bruce Eckel's two books, which contain the canonical answers to this question and to 74 similar FAQs.
The C++ community
Monday, August 29, 2005
 
 
Christopher Wells Send private email
Tuesday, August 30, 2005
 
 
I like the auto_ptr solution. It is a better coding technique indeed.
Dino Send private email
Tuesday, August 30, 2005
 
 
Thanks everyone for a most interesting discussion. I guess there isn't one perfect way of doing things. Effective C++ is now on my grocery list.

To "My Take On This Version" from Monday 29:
ok except for a slight drawback: code is being duplicated in the catch block and the destructor

To Dino:
"I would keep any exception throwing code out of constructors and destructors."
I agree with this and I think I will do a design based
on your init & shutdown, where the client must explicitly
call init before using the class, and shutdown after.

My reasons are:
- I prefer new to throw only for low memory reasons
- I prefer the destructor to do only the simplest stuff to be sure it never throws
- init and shutdown are the two unique places where I allocate or free resources
- it doesn't cost too much to add those calls where needed (I only have ONE client for now!)

I would also add a private boolean m_ShutdownHasBeenCalled, and throw from the destructor
if it's false, thus enforcing that all clients of TheClass call shutdown before destruction.

For added luxury, I would add a private boolean m_InitHasBeenCalled, and throw from
every member function if it's false, thus enforcing the init has been called prior
to calling the class.

I hope I won't find this design as a "so-stupid-never-do-this" example in those books I'm going to buy...

To be fair, auto_ptr seems tempting too but I dont have a firm grasp on that technique at this time and I prefer to understand what I'm doing.
Parisian developer ISO cute geek girl
Tuesday, August 30, 2005
 
 
It's not "so stupid, never do this", but two-stage initialization is (in the wider C++ community) considered to be a non-optimal solution.

Go with RAII all the way (i.e. shared_ptr/auto_ptr) and you'll be good. As good as you can be with C++, at least...
Robert 'Groby' Blum Send private email
Tuesday, August 30, 2005
 
 
Parisian: never throw from a destructor: http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.3

The smart-pointer approach is definitively the best. (I hope the identation doesn't go wild in the example below):

class MyClass {
  std::auto_ptr<Resource1> res1_;
  std::auto_ptr<Resource2> res2_;

  // declare as private, auto_ptr may not work as you
  // want on copy...
  MyClass & operator=(MyClass const &);
  MyClass(MyClass const &);

public:

  MyClass( )
    : res1_(new Resource1), res2_(new Resource2)
  { }

  ~MyClass( )
  { /* empty! */ }
};

Aside from any typping problems, this should close to the solution.

Change auto_ptr to a shared_ptr (as boost::shared_ptr) or change the operator=() and copy constructor to do a copy of the contents of the resources (a clone() method?) if you need copies of this object.
Whatever
Tuesday, August 30, 2005
 
 
So again ...

class Resource {
public:
    // Inconsistent construction
    Resource() { ... may throw }
    // Not exception safe destruction
    ~Resource() { ... may throw }
}

// RAII
class Consumer {
private:
    Resource resource;

public:
    Consumer() : resource() {
    }

    // May crash process when exception is thrown
    ~Consumer() {}
}


The main offender here is the resource which throws exception at the wrong times.

Maybe this makes everybody happy ...
This is not multi stage construction but it accounts for the states of the resource.

class Resource {
private:
  bool ready = false;
public:
    Resource() {}
    ~Resource() {}

    void init() {
        if(!ready) {
            ... may throw;
            ready = true;
        }
    )
    void shutdown() {
        if(ready) (
          ... may throw
          ready = false;
        }
    )
    void cleanup() {
      ... prevents a resource leak; should not throw exceptions
    }
    bool isReady () { return ready; }
}


// here is your RAII class. It does its job and guarantees
// no resource leaks and correct resource initialization and shutdown.
class Consumer {
private:
    Resource * resource;

public:
    Consumer() : resource(new Resource) {
        try {
          resource->init();
        } catch {
            resource->cleanup();
            delete resource;
            resource = NULL;
        }
    }

    ~Consumer() {
        if(resource != NULL) {
            try {
                resource->shutdown();
            } catch ( ... ) {
                resouce->cleanup();
            }
            delete resource;
        }
    }
}
Dino Send private email
Tuesday, August 30, 2005
 
 
Consumer() : resource(new Resource) {
        try {
          resource->init();
        } catch {
            resource->cleanup();
            delete resource;
            resource = NULL;
            throw;  // <----
        }
    }

If Resource initialization throws, I think this should be propagated.

But I still don't know why to write all this code when there are smart pointer classes around that do in essence the same thing...

(With the exception that, if Resource destructor throws, and you cannot change this situation, a wrapper that "shuts it up" would be okay...)
Whatever
Tuesday, August 30, 2005
 
 
Whatever: "never throw from a destructor"
Yeah I know that but in this case it is a blatant programming error that should never make it into the application. This is not an exception that you catch and do something with. By throwing from the destructor I hope to crash the app, making it obvious enough that it would be caught early in the dev cycle. (Hmm can you tell I'm new at this? Is there's a better way? Ok, other thread maybe.)

Anyway, I'm still hesitant... the smart pointer stuff does look interesting, I'm going to research this in the next few days.

Thanks again.
Parisian developer ISO cute geek girl
Tuesday, August 30, 2005
 
 
"can you tell I'm new at this? "

I did, that is why I've recommended the delete approach. Do investigate the shared_ptr/auto_ptr stuff, but when you use it make shure that you got a grasp on how shared_ptr reference count behaves(and how to avoid cyclic references) when using the = operator and copy constructor. Because if these don't count right when you do your stuff. The errors could be hard to track down. auto_ptr is a different beast, but works nicely when you use one object in a scope and don't do anything except call methods on it. Because auto_ptr is a simpler "device".
PPR
Tuesday, August 30, 2005
 
 
If your "resource" isn't just memory, say it's a C FILE handle, you could do something like this to make your custom handler which closes the file also.

Why! oh WHY! did they not just use the C++ file IO? I don't know, but this cleans up file handling without changing the IO.

#include <memory>

class CAuto_File
{
public:
    typedef FILE element_type;
    explicit CAuto_File(FILE *_P = 0) _THROW0()
        : _Owns(_P != 0), _Ptr(_P) {}
    CAuto_File(const CAuto_File& _Y) _THROW0()
        : _Owns(_Y._Owns), _Ptr(_Y.release()) {}
    CAuto_File& operator=(const CAuto_File& _Y) _THROW0()
        {if (this != &_Y)
            {if (_Ptr != _Y.get())
                {if(_Owns)
                    {fclose( _Ptr );}
                _Owns = _Y._Owns; }
            else if (_Y._Owns)
                _Owns = true;
            _Ptr = _Y.release(); }
        return (*this); }
    ~CAuto_File()
        {if (_Owns)
            fclose(_Ptr); }
    FILE& operator*() const _THROW0()
        {return (*get()); }
    // this is slightly dodgey, overloading the ones complement operator and using
    // it similarly to & operator because we still want the standard & to work and
    // the ~ operator doesn't have any operation otherwise - neater than calling get()
    FILE *operator~() const _THROW0()
        {return (get()); }
//    FILE *operator->() const _THROW0()
//        {return (get()); }
    FILE *release() const _THROW0()
        {((CAuto_File *)this)->_Owns = false;
        return (_Ptr); }
    // this is slightly dodgey, changing through a pointer to get around const
    // but the auto_ptr template does it, so we do also.
    bool isOpen() const _THROW0()
        {return (NULL!=get());}
    void close() _THROW0()
        {if (_Owns)
            {fclose( _Ptr );
             _Ptr=NULL;}
        _Owns = false; }
    void flush() _THROW0()
        {fflush( _Ptr );}
private:
    FILE *get() const _THROW0()
        {return (_Ptr); }
    bool _Owns;
    FILE *_Ptr;
};

the ~ operator was added so that you might call
CAuto_File foo;
fprintf( ~foo, "Hazah!" );
instead of
fprintf( foo.get(), "Hazah!" );
fprintf( &*foo, "Hazah!" ); // sometimes this is strangly optimised so thgat it tries just foo instead of calling *foo and getting the address.

there is an assumption that the file hasn't been closed otherwise by nefarious use of the ~ operator, and that the CAuto_File still owns it.  A safer implementation would bring all the file in out code into the class aswell, and thats overkill.



RAII - Resource Aquasition IS Initialisation...
Moving to a secondary call which "inits" the class is no longer RAII.

Tuesday, August 30, 2005
 
 
Exceptions and C++


Here's some stuff I wrote on this once:

http://www.arkestra.demon.co.uk/errors_cpp.html

Basically:
1) Never release/delete resources/memory by explicit calls
2) Get a good reference-counted smart template pointer and use it
3) Acquire resources in constructors, throwing exceptions on failure
4) Never throw exceptions from a destructor

There's an unfortunate shortage of good published writing on exceptions. I think Herb Sutter's 6 or so essays from "Exceptional C++", in particular, just confused things for a lot of people - it's just that the format didn't really work for that particular topic.

Anyway, follow the rules of thumb above and you'll be OK pretty much all of the time. People occasionally worry about reference-counted smart pointers getting in a cycle, but to be honest that's not been much of a problem with me to date. And I've worked on some pretty large bodies of code.


The problem with the original class


The problem with the OP's class is that it's using explicit calls to free resources. Each of those pointers should be "shared_ptr<Resource> m_Resource" rather than "Resource* m_Resource".

So instead of your original code you just have:

class CTheClass
{
public:
  CTheClass
  {
    m_Resource1 = new Resource1; // could throw but doesn't
    m_Resource2 = new Resource2; // could throw but doesn't
    m_Resource3 = new Resource3; // this throws
  }
private:
  shared_ptr<Resource1> m_Resource1;
  shared_ptr<Resource2> m_Resource2;
  shared_ptr<Resource3> m_Resource3;
}

No need for copy constructor, assignment operator or destructor - everything just plain works, even if the third element throws an exception.

Isn't that simpler?


Constructors and Exceptions


Read Bjarne Stroustrup's "Appendix E" for the definitive word on this. It's up on his website here (http://www.research.att.com/~bs/3rd_safe0.html).

It covers a lot of stuff to do with exceptions, but he devotes a few pages to the question:

  "Should you throw exceptions from constructors?"

Short answer: "Yes".

Longer answer: "The two-phase construction approach leads to more complicated invariants and typically to less elegant, more error-prone, and harder-to-maintain code. Consequently, the language-supported 'constructor' approach should be preferred to the 'init()-function approach' whenever feasible."


Not Just Theory


I use this approach myself, and it really works, for large, complex programs. Once you've got your smart pointer, you're sorted. I'd recommend boost's shared_ptr (http://www.boost.org/libs/smart_ptr/shared_ptr.htm).
Matthew Morris
Thursday, September 01, 2005
 
 
In java I have absolutely no problem with throwing exceptions from constructors. I do that all the time in fact.

In C++ though, throwing from constructors can easily end up in resource leaks. Therefore I would add state to objects and use two-phase construction. Then throw exceptions whenever the object receives messages in the wrong state. An alternative is using smart pointer to automatically cleanup resources; but smart pointers come for a price too. That is smart pointer related bugs are extremely hard to catch. The average Joe programmer won't find this sort of bug in a million years. With java it is less likely to leak resources (although not impossible).

Some of you guys have only best of the best on your development teams (hitting the high notes). That works for you as long as you are developing a product. As soon as you switch to maintenance (which is btw most time in a product life) star teams are not just useless but also unafordable.

Therefore, when you engineer your code you should target your biggest audience and that is the maintenance programmer. Write your code fail safe against the maintenance programmer. This is something guys like Bjarne and Joel won't tell you, perphaps because maintenance is not something they like to talk about.
Dino Send private email
Thursday, September 01, 2005
 
 
Dino, I disagree.

It's like saying that new fangled loop thing is hard to understand just copy and paste the code for each time you wish to do something.  If people don't understand resource allocation/management/deallocation they should be educated, not have the program "dummed down" and made more complicated.

"That is smart pointer related bugs are extremely hard to catch. "
Why? whats so hard? And Dammit I don't wanna be identified in the Top 5% of programmers while I'm doing this job I do!

While we are at it, we should ensure that people initialise instead of assign, especialy for
Parent classes
virtual base classes
constant member variables
reference member variables
non-const, nonreference member variables of user-defined type that have nondefault constructors.

and where possible for
non-const, nonreference member scalar variables, "normal variables".

Thursday, September 01, 2005
 
 
Matthew (arkestra),

Your text on smart pointers is the best description I've read so far, clear and concise.
You almost have me sold!

Thanks

(now I just have to figure out how to use this "Boost" thing)
Parisian developer ISO cute geek girl
Friday, September 02, 2005
 
 
"Why? whats so hard? And Dammit I don't wanna be identified in the Top 5% of programmers while I'm doing this job I do!"

Well, maybe you are :-)

What is the difference between auto_ptr and auto_ptr_ref?
Is there anything wrong with the following code?

void foo1(auto_ptr<int> value) {
  this->value = value;
}

void foo2(auto_ptr<int>& value) {
    this->value = value;
}

void main(void) {
    auto_ptr<int> value1(new int);
    auto_ptr<int> value2(new int);
    const auto_ptr<int> value3(new int);

    *value1 = 4;
    foo1(value);
    *value1 = 42;

    *value3 = 4;
    foo1(value);
    *value3 = 42;

    *value2 = 4;
    foo2(value);
    *value2 = 42;
}
Dino Send private email
Friday, September 02, 2005
 
 
Asides from some things that are either missing, or wrong.  Assume your passing value1 then value3 to foo1 and then value2 to foo2. Assuming the assignment within foo functions is also a auto_ptr assignment (whatever "this->value" is)

foo1 will take ownership of the pointer passed in - then this->value would hold it, otherwise on exit of the function the ptr would be deleted.

and so with the first set the assignment directly after should work - though using an auto_ptr which no longer owns the memory it's pointing at is bad.

you might think that the second set should throw compiler warnings because you "can't" change the content of the const auto_ptr, and so shouldn't be passing it to the non const function anyway.  As exampled in the Auto_File, the auto_ptr is dirty in this respect and you lose the ptr value in any case.

the third set using foo2 would have a similar problem though deferred by a step, that being that the by ref auto_ptr is going to hand over the ptr within the function instead of on entry.  the assignment afterwards is similarly poorly done.

Perhaps it's now time to compile and see what realy happens.

Monday, September 05, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz