views:

799

answers:

12

The C++ specification says the default destructor deletes all non-static members. Nevertheless, I can't manage to achieve that.

I have this:

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
        n = new N;
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N* n;
};

Then this should print the given message, but it doesn't:

M* m = new M();
delete m; //this should invoke the default destructor
+23  A: 

What makes you think the object n points to should be deleted by default? The default destructor destroys the pointer, not what it's pointing to.

Edit: I'll see if I can make this a little more clear.

If you had a local pointer, and it went out of scope, would you expect the object it points to to be destroyed?

{
    Thing* t = new Thing;

    // do some stuff here

    // no "delete t;"
}

The t pointer is cleaned up, but the Thing it points to is not. This is a leak. Essentially the same thing is happening in your class.

Fred Larson
@OP: Because it has no way of knowing that it's okay to do that. You could have a pointer to something that should not be destroyed (in fact, it'll be quite common for that to be the case).
T.J. Crowder
also even if you add in the commented out bit you may need to flush the stream brefore you actually see output
jk
@T.J. Crowder: but still: isn't destroying the object what the specification requires?@jk: why whould I need to flush the stream?
Oszkar
@Oszkar: the specification only requires automatic objects to be destroyed... In this case, the pointer *is* destroyed as soon as it goes out of scope. But what if `some stuff` transmitted the pointer to another object/function that does not go out of scope ? Should the `Thing` still be destroyed ?
RaphaelSP
+10  A: 

Imagine something like this:

class M {
public:
    M() { }
//  ~M() {        // If this happens by default
//      delete n; // then this will delete an arbitrary pointer!
//  }
private:
    N* n;
};

You're on your own with pointers in C++. No one will automatically delete them for you.

The default destructor will indeed destroy all member objects. But the member object in this case is a pointer itself, not the thing it points to. This might have confused you.

However, if instead of a simple built-in pointer, you will use a smart pointer, the destruction of such a "pointer" (which is actually a class) might trigger the destruction of the object pointed to.

Pavel Shved
Which is why smart pointers exist, so that there will be automatic destruction.
David Thornley
"The default destructor will indeed destroy all member objects. But the member object in this case is a pointer itself, not the thing it points to. This might have confused you." -> Indeed. That clarifies it. Thanks! :)
Oszkar
@Oszkar: I think you can un-accept an answer and then accept another. It seems that this one is more appropriate !
RaphaelSP
@RaphaelSP: I got several appropriate (and very nice!) answers, and decided to mark the first one, because it was hard to decide.
Oszkar
+7  A: 

The default destructor is destroying the pointer. If you want to delete the N with M's default destructor, use a smart pointer. Change N * n; to auto_ptr<N> n; and n will be destroyed.

Edit: As pointed out in comments, auto_ptr<> is not the best smart pointer for all uses, but it looks like what's called for here. It specifically represents ownership: the N in an M is there for the duration of the M and no longer. Copying or assigning an auto_ptr<> represents a change in ownership, which is usually not what you want. If you wanted to pass a pointer from M, you should pass a N * gotten from n.get().

A more general solution would be boost::shared_ptr<>, which will be in the C++0x standard. That can be used pretty much wherever a raw pointer would be used. It's not the most efficient construct, and has problems with circular references, but it's generally a safe construct.

Another edit: To answer the question in another comment, the standard behavior of the default destructor is to destroy all data members and base classes. However, deleting a raw pointer simply removes the pointer, not what it points to. After all, the implementation can't know if that's the only pointer, or the important one, or anything like that. The idea behind smart pointers is that deleting a smart pointer will at least lead to the deletion of what it points to, which is usually the desired behavior.

David Thornley
You should really make sure you understand what is going on before using auto_ptr. Its copying and assignment semantics may not be what you would expect.
Scott Wolchok
@David: I do understand that my example is not a wise design solution. I'm just trying to figure out the standard behaviour of default destructors. I would use tr1::shared_ptr (it's already included in gcc) or just store the object by value.Sorry for not being specific enough with this question.
Oszkar
@Oszkar: Yes, and the standard behavior is to delete the elements and base classes. I'm sorry I didn't make that clearer. I'll edit again.
David Thornley
+4  A: 

Your argument might seem sound but that's not how things work for pointers.

n is actually being destructed but, what this means is that the N* destructor is being called which, it does NOT destruct whatever object n is pointing to. Think of the N*'s destructor as if it were an int's destructor. It deletes its value, the same happens for a pointer, it deletes the address it is pointing to, but it doesn't need to delete whatever object is located at the address you just deleted.

Anzurio
Yeah, that answers it. Thanks!
Oszkar
+6  A: 

Is there any reason why you use a pointer when the pointed-to object seems to belong the contained object? Just store the object by value:

class M
{
    N n;

public:

    M() : n()
    {
    }
};
FredOverflow
Of course that's the way to go. I'm just testing the behaviour of the default destructor. And it appears to me that it doesn't behave right.EDIT: after reading all the answers, it does :)
Oszkar
+1  A: 

Try avoiding using pointers. They are last resort elements.

class N {
public:
    ~N() {
        std::cout << "Destroying object of type N";
    }
};

class M {
public:
    M() {
       // n = new N; no need, default constructor by default
    }
//  ~M() { //this should happen by default
//      delete n;
//  }
private:
    N n; // No pointer here
};

Then use it this way

main(int, char**)
{
    M m;
}

This will display Destroying object of type N

Tristram Gräbener
Pointers are vital in C++ for many purposes. Know how to use them well.
David Thornley
+2  A: 

I think you may be confused about levels of indirection here. When an instance is destroyed, each data member does indeed get destroyed along with it. In your case, when an M is destroyed and M::~M() is called, its variable n really is destroyed. The problem is that n is a N *, so while the pointer is destroyed, the thing it points to is not.

delete does not work like this. Consider your simple statement:

delete n;

The above statement destroys the thing that n points to, which is an object of type N. It does not destroy n itself, which is an N * pointer.

There is a very good reason that M::~M() does not automatically call delete n; which is this: the N object referred to might be shared between several M objects, and if one M were destroyed, the rest would lose the N they were pointing at, leaving horrible dangling pointers everywhere. C++ does not attempt to interpret what you meant to do with your pointers, it just does what you told it to do.

In short, M really is destroying all of its members when it is destroyed, it's just that this destruction doesn't do what you think it should do. If you want a pointer type which takes ownership of an object and destroys it when the pointer is destroyed, look at std::auto_ptr.

Philip Potter
IMO, this is the clearest answer.
Paul Nathan
Yes, that's a very informative explanation. Thanks!
Oszkar
+1  A: 

The default destructor looks like this:

~M()
{
}

The default destructor does not insert code to do anything with pointed-to things. What if you had n pointing to a stack variable? Automatically inserting a delete n would crash.

The default destructor calls the destructor on each member of the class (member.~T()). For a pointer, that's a no-op (does nothing), just like myint.~int() does nothing, but for member classes with defined destructors, the destructor is called.

Here's another example:

struct MyClass {
public:
    MyClass() { .. } // doesn't matter what this does

    int x;
    int* p;
    std::string s;
    std::vector<int> v;
};

The default destructor in reality is doing this:

MyClass::~MyClass()
{
    // Call destructor on member variables in reverse order
    v.~std::vector<int>(); // frees memory
    s.~std::string();      // frees memory
    p.~int*();             // does nothing, no custom destructor
    x.~int();              // does nothing, no custom destructor
}

Of course, if you define a destructor, the code in your destructor runs before the member variables are destroyed (obviously, otherwise they would not be valid!).

AshleysBrain
That's really informative. Thanks!
Oszkar
+3  A: 

It is incorrect to say that the destructor deletes members. It invokes the destructor of each member (and base class), which for built-in types (like pointers) means doing nothing.

Matching news with deletes is your responsibility (either manually, or with the help of smart pointers).

UncleBens
+1  A: 

I think you could benefit from a very simple example:

int main(int argc, char* argv[])
{
  N* n = new N();
} // n is destructed here

This will not print anything either.

Why ? Because the pointer (n) is destructed, not the object pointed to *n.

Of course, you would not want it to destroy the object pointed to:

int main(int argc, char* argv[])
{
  N myObject;
  {
    N* n = &myObject;
  } // n is destructed here, myObject is not

  myObject.foo();
} // myObject is destructed here

You should remember that unlike languages like C# or Java, there are 2 ways to create objects in C++: directly N myObject (on the stack) or via new like in new N() in which case the object is placed on the heap and YOU are reponsible for releasing it at a later time.

So your destructor destroys the pointer, but not the object pointed to. Allocate the object without new (and without using a pointer) or use a Smart Pointer if you want it to be automatic.

Matthieu M.
The first example does point out very clearly how the destructor works. Thanks.
Oszkar
A: 

M destructor should have 'delete n'.

james
A: 

Since you are using new to create instance, it won't delete by default.

fastcodejava