views:

229

answers:

7

What potential memory leaks won't an implicit destructor handle? I know that if you have anything stored on the heap it won't handle it, and if you have a connection to a file or a database, that needs to be handled manually. Is there anything else? What about, say, non-base data types like vectors?

Also, in an explicit destructor, need you destroy non-heap variables which would have been destroyed by the implicit, or are they handled automatically?

Thanks

+4  A: 

vectors and the like will deallocate themselves because their destructor is called.

In fact your problem will lie with anything that would cause a problem of a dangling pointer (or handle or whatever), ie anything that needs to be manually Closed or de-allocated won't be in an implicit destructor. Anything that destructs itself properly will be fine. This is why the RAII idiom is so popular :)

Goz
+1  A: 

The implicit destructor calls the destructor on all member variables.

If you have a raw pointer, its destructor does nothing. So if you own the memory it points to, you have to release it explicitly.

The same applies to any other resource, or any other action you wish to take not implied by the default destructor of the member variable.

The default destructors of members are still called, so vectors, auto_ptrs, files opened using std streams or other C++ libraries all get destroyed. The moral is to wrap any OS objects which need releasing in C++ classes which tidy themselves up, so that application classes don't have to worry about it.

Pete Kirkham
Not exactly. The destructor actually destroys raw pointers like anything else; it does not destroy the pointed object, though.
Gorpik
The destructor of the class calls the destructor of the raw pointer. The destructor of the raw pointer does nothing. A raw pointer is not 'destroyed'; nothing happens to it or to whatever it references during destruction.
Pete Kirkham
A: 

A class' destructor will implicitely call the destructors of all non-static members, then the destructors of virtual base classes, then its own code.

If you have STL containers as class members, you don't have to do anything explicitely--they will free their own memory in their destructors.

Nikola Gedelovski
Unless the STL containers contain raw pointers; in this case, you may need to destroy the pointed objects explicitly.
Gorpik
A: 

You're already making wrong assumptions. If my class holds heap-allocated data using an std::auto_ptr<Data>, the implicit dtor will handle it. No memory will be leaked. The reason is that the implict dtor, like any other dtor, will call the dtors of all members and base classes.

Now, a good class design holds all important resources as members with proper dtors. As a result, the dtor body needs to do nothing to prevent resource leaks. Of course, there is one exception to that rule: resource managing classes themselves manage precisely one resource, and therefore clean up that one resource in their dtor.

MSalters
+3  A: 

I think the question is upside-down. Don't think in terms of what object destruction doesn't do: think in terms of what it does do.

If a class only has an implicit destructor, then when it is destroyed, all non-static data members are destroyed too, as are all base-class sub-objects.

You can argue a bit whether this is done "by the implicit destructor" or not - if you write a destructor, those things still happen. It's part of the object destruction process itself, rather than part of the code of the destructor as such.

So, if your "connection to a file" is a FILE* class member, then the default destructor doesn't release it, because FILE* is a C thing, and it doesn't have a destructor. Destroying a FILE* does nothing. If your "connection to a file" is a std::ofstream, then it does have a destructor, and the destructor tries to flush and close the connection.

Vector has a destructor which release the vector's resources. This means that in turn the elements in the vector have their destructors called.

For each member variable you have, you should look at the corresponding documentation to see how resources are handled for that type. Obviously with experience, you start to remember the answers.

If the member variable is a pointer, then the implicit destructor doesn't do anything with it. So if it points to heap-allocated memory, and this object holds the only pointer to that memory, then you need to free it to avoid a memory leak.

If you find yourself writing a destructor in C++ that needs to free more than one thing, then you've probably designed the class badly. There are almost certainly exception-safety errors elsewhere in the class: it's possible to get the code right, but if you're experienced enough to get it right then you're experienced enough to make your own life easier by designing it differently ;-). The solution is to write classes with a single responsibility -- to manage a single resource -- and use them as member variables. Or, better, to find library classes that manage the resource you're using. shared_ptr is pretty flexible.

"need you destroy non-heap variables"

There is no such thing in C++ as "heap" and "non-heap" variables. Destroying a pointer does not free the thing pointed to, for the obvious reason that nothing in the type of a pointer tells you how the thing it points to was allocated, or who "owns" it.

Steve Jessop
A: 

What potential memory leaks won't an implicit destructor handle? I know that if you have anything stored on the heap it won't handle it, and if you have a connection to a file or a database, that needs to be handled manually. Is there anything else? What about, say, non-base data types like vectors?

To put it simply, you are correct. The only thing not handled by the implicit destructor is memory allocated in the form of a pointer or another type of resource that needs to be released explicitly.

With regard to vectors or any other class type objects; all classes have a destructor which takes care of releasing their data. The destructor of an object of this type is called when it goes out of scope. All basic data types like: int, float, double etc are released in similar fashion.

Also, in an explicit destructor, need you destroy non-heap variables which would have been destroyed by the implicit, or are they handled automatically?

The answer to this is that they are handled automatically, in fact you should never ever try to explicitly call the destructor of an object.

In an implicit destructor the following occurs:

  • Each of the member variables of the class have their destructors called in turn.

In an explicit destructor the following occurs:

  • The body of the explicit destructor is executed
  • Each of the member variables of the class have their destructors called in turn.

So you can see that an explicit constructor is much the same as an implicit one, except that you can take any necessary manual intervention.

Now as a bit of advice with regard to managing memory allocated objects you should pretty much always use RAII (resource acquisition is initialisation). The crux of this is smart pointers, these are pointers that are deleted correctly when they go out of scope just like non heap allocated objects. Ownership becomes an issue once you start using them but that's a story for another day. A good place to start with smart pointers is boost::shared_ptr. (btw if you haven't got on board with boost yet and you write c++ code do yourself a favour...)

radman
+1  A: 

You need to understand three things about destructors. Say you have an object t of class T with three members.

class T
{
    A a;
    B b;
    C c;

    // ...

} t;
  1. Destructing t ALWAYS means calling the following destructors in that order: ~T(), ~C(), ~B(), ~A(). You cannot influence these semantics. It is impossible to prevent the destruction of the members. This ALWAYS happens, no matter if you define ~T() manually or if the compiler generates it.

  2. Implicitly generated destructors are ALWAYS a no-op. However, as stated in point 1, the destructors ~C(), ~B() and ~A() will still be executed afterwards.

  3. The destructors of scalar types, especially C-style pointers, are ALWAYS a no-op. This is the sole reason why you almost always need to write the destructor (and the copy constructor and the assignment operator) manually when you have a C-style pointer member -- after ~T() has finished, destructing a C-style pointer member does NOTHING, so any cleanup intended on the pointees has to be done in ~T().

I hope this clears things up for you.

FredOverflow