Game Development Community

TVector and constructors

by Phil Shenk · in Torque Game Builder · 12/23/2005 (9:51 pm) · 5 replies

In the .h file for tVector, there is the warning:
/// This template does not initialize, construct or destruct any of
/// it's elements.  This means don't use this template for elements
/// (classes) that need these operations.  This template is intended
/// to be used for simple structures that have no constructors or
/// destructors.
Now my question is, what exactly does this mean? Doesn't every C++ type, including the built-in types have by nature a constructor and destructor? I've been using it for t2dVectors, which aren't really simple types... does this mean that I should take care of creating them before adding to the Vector, and destroying them when I'm done?

I sort of asked this question before, but I'm still kind of confused about what I should be doing in regards to this warning.

Thanks, Happy Holidays :)

#1
12/24/2005 (12:28 pm)
TVector really doesn't call constructors/destructors? Hmm.

Anyway, if you're wondering how this code bypasses constructors/destructors, consider the following code:

SomeClass *pTheVar = (SomeClass*)new char[sizeof(SomeClass)];

//pTheVar never had a constructor called on it, but you can access members and so forth.

delete [](char*)pTheVar; //This will not call SomeClass::~SomeClass, since it is deleting a char* array.

This is pretty shadey stuff and should only be done if you really know what you're doing. And, even then, you should have a very good reason. Now, tVector could call constructors/destructors; the code for doing so would be like this:

SomeClass *pTheVar = (SomeClass*)new char[sizeof(SomeClass)];
new(pTheVar) SomeClass(/*Constructor Parameters go here*/); //Placement new

//The placement new operator correctly called the constructor specified by the parameters.

pTheVar->~SomeClass(); //Call the destructor directly as a method.
delete [](char*)pTheVar; //Deallocate the memory for pTheVar.

I suspect one reason tVector doesn't do this is because, in a container class, it would have to enforce upon every class used in it to be fully copyable (the way std::vector does). That is, every class you use it on would need a copy constructor, destructor, and a copy assignment operation (unless the C++ defaults of just copying the memory are OK). The code to use these is less trivial than the regular code, and it requires that a lot of functions take a SomeClass & to be the source object for the copy.

What you should do with regard to this warning is never use a tVector of an object that has a constructor or destructor. Note that you can still have tVectors of pointers to objects, but you shouldn't have them of the objects themselves:

tVector<SomeClass> thisIsBad;
tVector<SomeClass*> thisIsGood;

Also note that, because "thisIsGood" is a vector of object pointers, you must do the memory management yourself.
#2
12/24/2005 (2:11 pm)
So, basically, just use Vector for pointers to objects, or for simple built-in types? Looking through the t2d code, it seems that most(all?) cases of Vector usage are storing pointers only.

Then, create and destroy the objects that the pointers are referencing myself, right?

what about something sinple, like:
class Coord
{
public:
   U32 x;
   U32 y;
   F32 heuristic;
};
That should all work with the C++ automatic constructors, destructors and member-wise assignment copying, right?

Do I really have to memory manage these? There's no easy way to do that, is there?

Thanks
#3
12/24/2005 (11:15 pm)
What you should do with regard to this warning is never use a tVector of an object that has a constructor or destructor. Or, as the reverse, if an object has a constructor or destructor, you must not use it in a tVector.
#4
12/25/2005 (2:05 pm)
Thanks
#5
12/27/2005 (11:01 am)
The lack of constructor/destructor support in the TGE vector template can be a pain for objects. A method that is used in TGE although not much nicer than Smaugs suggestion is to use "constructInPlace()" and "destructInPlace()". Allocate a vector of pointers as normal and push/pop elements but call "construct/destructInPlace()" to actually allocate/deallocate them.

You've obviously got to be careful before using something like "clear()" in that you iterate the list first and deallocate the objects.

Still nasty though; don't blame me, I didn't write them. ;)

Here they are:-

template <class T>
inline T* constructInPlace(T* p)
{
   return new(p) T;
}

template <class T>
inline T* constructInPlace(T* p, const T* copy)
{
   return new(p) T(*copy);
}

template <class T>
inline void destructInPlace(T* p)
{
   p->~T();
}

- Melv.