views:

1447

answers:

5

In a related question I asked about creating a generic container. Using polymorphic templates seems like the right way to go.

However, I can't for the life of me figure out how a destructor should be written. I want the owner of the memory allocated to be the containers even if the example constructor takes in an array of T (along with its dimensions), allocated at some other point.

I would like to be able to do something like

MyContainer<float> blah();
...
delete blah;

and

MyContainer<ComplexObjectType*> complexBlah();
...
delete complexBlah;`

Can I do something like this? Can I do it without smart pointers?

Again, thanks for your input.

A: 

It can be done, but this is pretty advanced stuff. You'll need to use something like the boost MPL library (http://www.boost.org/doc/libs/1_36_0/libs/mpl/doc/index.html) so that you can get MyContainer's destructor to select the right kind of destructing it will need to do on individual items on the container. And you can use the boost TypeTraits library to decide what kind of deleting is required (http://www.boost.org/doc/libs/1_36_0/libs/type_traits/doc/html/index.html). I'm sure it will have a trait that will let you decide if your contained type is a pointer or not, and thus decide how it needs to be destructed. You may need to implement traits yourself for any other types you want to use in MyContainer that have any other specific deletion requirements. Good luck with it! If you solve it, show us how you did it.

Scott Langham
+3  A: 

I'd recommend if you want to store pointers to complex types, that you use your container as: MyContainer<shared_ptr<SomeComplexType> >, and for primitive types just use MyContainer<float>.

The shared_ptr should take care of deleting the complex type appropriately when it is destructed. And nothing fancy will happen when the primitive type is destructed.


You don't need much of a destructor if you use your container this way. How do you hold your items in the container? Do you use an STL container, or an array on the heap? An STL container would take care of deleting itself. If you delete the array, this would cause the destructor for each element to be executed, and if each element is a shared_ptr, the shared_ptr destructor will delete the pointer it itself is holding.

Scott Langham
I was doing an array on the heap, but I might go to an STL vector. A related question would then be "how does the STL do it?".
Voltaire
STL containers will destruct the elements they contain when they themselves are destructed. STL containers won't take ownership of a pointer. Who knows what a pointer points at, it's not necessarily something that requires deleting. Eg, if you have a picture in memory, you may have pallete info
Scott Langham
stored within it, with a pointer pointing at that. This isn't owned by the pointer, and shouldn't be delete'd. So with pointers that you know point at memory you allocated, you always have to delete them yourself. shared_ptr's are different, they are intended to be used where the object they manage
Scott Langham
needs some kind of cleanup (usually, but not always by performing a delete).
Scott Langham
it's tricky to fit the answer in comments, you should create a new question if you haven't already!
Scott Langham
So it would seem. The 300 character limit is kind of awkward.
Voltaire
A: 

If you don't want to go with smart pointers you can try partial template specialisation, it let's you write a template that is only used when you instatiate a container with a pointer type.

Harald Scheirich
+1  A: 

You most probably do want to use smart pointers here, it really simplifies the problem. However, just as an excercise, it's quite easy to determine if given type is pointer. Rough implementation (could be more elegant, but I dont want to introduce int2type):

typedef char YesType;
typedef char NoType[2];

template<typename T>
struct IsPointer
{
typedef NoType Result;
};
template<typename T>
struct IsPointer<T*>
{
typedef YesType Result;
};

template<typename T>
struct MyContainer
{
~MyContainer()
{
 IsPointer<T>::Result r;
 Clear(&r);
 delete[] data;
}
void Clear(YesType*)
{
 for (int i = 0; i < numElements; ++i)
  delete data[i];
}
void Clear(NoType*) {}

T* data;
int numElements;

};

yrp
That answer is almost perfect. It does correctly delete pointers and primitive data types. However, I can't seem to make it valgrind-error-free. The valgrind error is rather obtuse, but I will keep playing with it to see if I can figure out what is going on.
Voltaire
A: 

delete is used to deallocate memory previously allocated with new. You do not need to use delete here, when blah and complexBlah go out of scope they will automatically be destroyed.

While yrp's answer shows you one way of using template specialization to delete the objects contained if they are pointers, and not if they aren't, this seems like a fragile solution. If you want behavior like this you are better off using Boost Pointer Container libraries, which provide this exact behavior. The reason that the standard library doesn't is because the containers themselves don't know if they control the contained pointer or not - you need to wrap the pointer in a type that does know - ie a smart pointer.

Greg Rogers