views:

285

answers:

8

Cosider the following code:

class Foo
{
    Monster* monsters[6];

    Foo()
    {
        for (int i = 0; i < 6; i++)
        {
            monsters[i] = new Monster();
        }
    }

    virtual ~Foo();
}

What is the correct destructor?

this:

Foo::~Foo()
{
    delete [] monsters;
}

or this:

Foo::~Foo()
{
    for (int i = 0; i < 6; i++)
    {
        delete monsters[i];
    }
}

I currently have the uppermost constructor and everything is working okey, but of course I cannot see if it happens to be leaking...

Personally, I think the second version is much more logical considering what I am doing. Anyway, what is the "proper" way to do this?

+10  A: 

delete[] monsters;

Is incorrect because monsters isn't a pointer to a dynamically allocated array, it is an array of pointers. As a class member it will be destroyed automatically when the class instance is destroyed.

Your other implementation is the correct one as the pointers in the array do point to dynamically allocated Monster objects.

Note that with your current memory allocation strategy you probably want to declare your own copy constructor and copy-assignment operator so that unintentional copying doesn't cause double deletes. (If you you want to prevent copying you could declare them as private and not actually implement them.)

Charles Bailey
+2  A: 

Your second example is correct; you don't need to delete the monsters array itself, just the individual objects you created.

Carl Norum
+8  A: 

For new you should use delete. For new[] use delete[]. Your second variant is correct.

Kirill V. Lyadvinsky
+4  A: 

The second one is correct under the circumstances (well, the least wrong, anyway).

Edit: "least wrong", as in the original code shows no good reason to be using new or delete in the first place, so you should probably just use:

std::vector<Monster> monsters;

The result will be simpler code and cleaner separation of responsibilities.

Jerry Coffin
least wrong? Please elaborate.
Jasper
@Jerry I assume that `Monster` is a polymorphic type. In that case, some sort of pointer has to be used as the element type of the `vector` to enable polymorphism.
FredOverflow
@FredOverflow: while it's certainly possible that he could be dealing with a polymorphic hierarchy, 1) he hasn't actually shown that, and 2) a vector will still be fine if it is.
Jerry Coffin
@Jerry 1) Right 2) Absolutely
FredOverflow
I am using a private array of constant length, with the members being created in the constructor and deleted in the destructor, and not being replaced in between.In such a case, I don't really see how there is an advantage to using a vector over a c array. Am I missing something?
Jasper
@Jasper: yes. For example, what will happen if `new` fails (and throws an exception) when you're trying to allocate the fourth `Monster` in your array?
Jerry Coffin
@Jasper Is Monster itself a base class or not?
FredOverflow
If Monster is a base class and it does not want to share its monsters with other Foo objects then it should be boost::ptr_vector<Monster> if not then an array of Monster objects (not monster pointers).
Martin York
@FredOverflow No, Monster is not a base class. I was using pointers because the Foo shares its Monsters with member variables (MonsterDisplayers)
Jasper
@Jasper Then `std::vector<Monster>` without any pointers is really the easiest and "most C++" solution, as suggested by Jerry.
FredOverflow
This is getting way beyond the scope of the question, but in that case, would a reallocation not cause all pointers to the monsters (which were given to member variables) to become invalid?
Jasper
@Jasper:yes -- but since you're starting from a fixed-size array, a reallocation should never happen unless you alter the whole situation so that size is dynamic.
Jerry Coffin
True. I was thinking ahead - this very question came to me through "dummy content" I had created for my GUI system to display, now I was thinking about another similar case I would be facing in the real program, which will not have a fixed size (though, then again, it could probably work out just fine, as once set up, the "array" should not any new items (nor should items be erased).
Jasper
@Jasper:yes, if there's a possibility of resizing, you'd be better off passing around an index into the vector instead of a pointer to the object in the vector.
Jerry Coffin
@Jerry Coffin: but that would mean I would have to pass around the vector itself to about everywhere as well - I must say I don't quite like that either.Additionally, the `MonsterDisplayer`s should be able to handle monsters from other sources as well (think of them as temporary monsters). Nah, that's not the solution either. Thanks for thinking about this with me, though.
Jasper
@Jasper: if you need to have pointers/iterators to the objects remain valid across resizing of the container, a `vector` probably isn't the best choice of container. Assuming it stays somewhere around 6 items, a `list` might be a reasonable choice.
Jerry Coffin
It will somewhere around 40 elements, but I guess that a list still a good option in that case. (actually, when I wrote that last comment yesterday, I thought "I guess I would need a linked list" but I didn't know if STL provided one and searched for it in the wrong way and coudn't find it... silly me)
Jasper
@Jasper:at 40 items, I'd *prefer* not to use a list. Depending on the pattern of insertions (and deletions) you might be better off with an `std::deque`. Its rules about when iterators are invalidated are more complex, but access is almost as fast as in a `vector`.
Jerry Coffin
Basically, there is the initialization of the data and then there's no more insertions or deletions, until destruction. However, as the only way I will be accessing the data is in a loop from n=0 to n=size, I didn't think that the performance would be all that different... Actually, as far as I know, unless you are using "at" (or an overloaded operator with the same effect) there is not much of a disadvantage to lists, is there?
Jasper
Let me esplain a little more. First I create my Monster and put them in my "array" (I am not sure yet which container I will use) and then I start handing out Monster* to member variables (let's say `myMonsterCollection` and `yourMonsterCollection`) some of those pointers might get copied (to `MonsterDisplayers`) and they might move from my collection to your collection and vice versa (in actuality there are more than two locations). I run a loop over the "array" to see which monsters want to register events. The original "array" is then really only kept as the "owner". Is this a good design?
Jasper
@Jasper: it sounds like a perfectly reasonable design -- and as long as don't hand out any pointers until *after* you've added all the monsters to the collection, using a vector won't be a problem; pointers/iterators into a vector can *only* be invalidated when you call something like push_back, or insert.
Jerry Coffin
A: 

You delete each pointer individually, and then you delete the entire array. Make sure you've defined a proper destructor for the classes being stored in the array, otherwise you cannot be sure that the objects are cleaned up properly. Be sure that all your destructors are virtual so that they behave properly when used with inheritance.

Eric H
I think doing both would be rather strange, as the array is of a constant length, so you need not delete it. Also, I did declare the destructor virtual, so that comment was pretty useless.
Jasper
A: 

If you're not sure in this - or a similar - case, it's very simple to make a quick check: insert a printf() into the destructor.

I think delete[] is more C++-ish, but I also vote for one-by-one delete:

  • maybe later some method should be called before destruction,
  • some sleep should be inserted for debugging or other reason,
  • the destruction must be performed in specific order,
  • later, delete will be changed to passing stuff to a garbage collector.
ern0
`delete[]` is just plain wrong.
rlbond
+2  A: 

delete[] monsters is definitely wrong. My heap debugger shows the following output:

allocated non-array memory at 0x3e38f0 (20 bytes)
allocated non-array memory at 0x3e3920 (20 bytes)
allocated non-array memory at 0x3e3950 (20 bytes)
allocated non-array memory at 0x3e3980 (20 bytes)
allocated non-array memory at 0x3e39b0 (20 bytes)
allocated non-array memory at 0x3e39e0 (20 bytes)
releasing     array memory at 0x22ff38

As you can see, you are trying to release with the wrong form of delete (non-array vs. array), and the pointer 0x22ff38 has never been returned by a call to new. The second version shows the correct output:

[allocations omitted for brevity]
releasing non-array memory at 0x3e38f0
releasing non-array memory at 0x3e3920
releasing non-array memory at 0x3e3950
releasing non-array memory at 0x3e3980
releasing non-array memory at 0x3e39b0
releasing non-array memory at 0x3e39e0

Anyway, I prefer a design where manually implementing the destructor is not necessary to begin with.

#include <array>
#include <memory>

class Foo
{
    std::array<std::shared_ptr<Monster>, 6> monsters;

    Foo()
    {
        for (int i = 0; i < 6; ++i)
        {
            monsters[i].reset(new Monster());
        }
    }

    virtual ~Foo()
    {
        // nothing to do manually
    }
};
FredOverflow
A: 

It would make sens if your code was like this:

#include <iostream>

using namespace std;

class Monster
{
public:
        Monster() { cout << "Monster!" << endl; }
        virtual ~Monster() { cout << "Monster Died" << endl; }
};

int main(int argc, const char* argv[])
{
        Monster *mon = new Monster[6];

        delete [] mon;

        return 0;
}
Daniel