Custom vector class, destructors and constructors

Started by
8 comments, last by Lck 18 years, 4 months ago
Hello everyone, I recently tried to recode a vector class (like the STL vector) from scratch, using C++ templates. Basically, the class has some functions which allow the user to insert elements, delete them, and so forth... The data structure holding the objects is doubled if needed and shrinks back dynamically. This is the code:
template <class F> int vector<F>::reserve(unsigned long size){
	F *pNew;

	if(size <= iReserved)
		return iReserved;
	else{
		pNew = new F[size];

		if(pNew == NULL)
			return 0;

		memcpy(pNew, vData, sizeof(F) * iFilled);	//Copy filled data cells

		delete [] vData;
		vData = pNew;

		iReserved = size;

		return size;
	}
};
This all works perfectly with "simple" data types, but if I instanciate a vector storing another class, which has its own constructors and destructors, the delete[] operator automatically calls the destructor of all allocated objects, invalidating the data stored in the new array. Is there any way to prevent new and delete from calling the destructor? Should I use malloc and free instead? (doesn't seem right, to me) I could as well allocate an array of pointers to the data type specified by the user, but I fear that would waste the benefits of using an array: direct-access of data, locality of reference (cache), and so on... Is there another solution? Thank you very much :-)
Advertisement
You could try using std::copy() instead of memcpy().
the std::vector allows for this to happen - it also copies data across in a way that causes the constructors to be called for the instances in the new vector. The class being stored in the vector is responsible for ensuring that this is OK, by implementing a proper copy constructor and destructor if needed.

You might do well to crack open your compiler's implementation of std::vector, if you can find it :)
I'm pretty sure a std::vector never actually shrinks. Your best bet is to swap it with another, smaller vector. But that's beside the point...

You could go the placement new/delete route. That lets you allocate a chunk of memory (using new, not malloc) without calling the constructor. Then you control when the constructor gets called. Likewise for delete.

However you certainly do want to call the object's constructor at some point. And calling a constructor without ever calling a destructor is not wise. So trying to remove the destructor from the picture is perhaps not a good solition. Zahlman mentioned this earlier so I'm just saying "I agree."
I'd love to know where you got the idea that 'unsigned long' is for size. Use std::size_t. You should never use 'memcpy' to copy an object - that's what copy constructors are for. Open up the standard and read about what a std::vector is supposed to do, then open up the source code for an implementation and study it.
One problem here is that reserve() does not create elements -- it simply allocates space. Because of that, "new F[size]" is wrong. In reserve(), you should use "new char[size*sizeof(F)]" and then use placement new in push_back() and resize().
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Several points:


  • You need to seperate memory allocation and memory deallocation from object construction/destruction. Use operator new and operator delete for memory and use placement new and explicit destructor calls for object construction/destruction

  • memcpy can't be used for any class that have a non-trival copy constructor - you need to call the copy constuctor using placement new instead

  • your reserve(size) function actually resizes your vector, which it shouldn't

  • if you're actually trying to code an implementation of std::vector, then you're missing support for allocators.


Quote:Original post by SiCrane
You could try using std::copy() instead of memcpy().

Quote:Original post by RDragon1
I'd love to know where you got the idea that 'unsigned long' is for size. Use std::size_t. You should never use 'memcpy' to copy an object - that's what copy constructors are for. Open up the standard and read about what a std::vector is supposed to do, then open up the source code for an implementation and study it.


Well, I'm not trying to code a standard vector, I'm just trying to build a very simple class to handle arrays. Thus I'm not using std:copy() nor std:size_t.
Should I replace 'memcpy' by an assignment cycle (which should call the copy constructor)?

Quote:Original post by JohnBolton
One problem here is that reserve() does not create elements -- it simply allocates space. Because of that, "new F[size]" is wrong. In reserve(), you should use "new char[size*sizeof(F)]" and then use placement new in push_back() and resize()

Perfect, that was the kind of solution I was looking for. Then I'll have to call new and delete only on the functions that actually insert or remove items on the vector.

Thank you very much for your help.
Quote:Original post by Lck
Well, I'm not trying to code a standard vector, I'm just trying to build a very simple class to handle arrays. Thus I'm not using ... std:size_t.

While you might not want to use std::size_t, you should definately be using some sort of typedef'd type. Something like:
template<typename T> vector {   typedef unsigned long size_type;   size_type size();   void reserve(size_type);   ...};

That way, you avoid potentially dangerous casts later down the road, and provide additional type information to the end users. The compiler might not distinguish between vector<T>::size_type and unsigned long, but it still provides information to readers.

Also, rename the class to something else. The last thing you need is a test going haywire because you accidentally used a standard vector instead of your custom one.

CM
Quote:Original post by Conner McCloud
That way, you avoid potentially dangerous casts later down the road, and provide additional type information to the end users. The compiler might not distinguish between vector<T>::size_type and unsigned long, but it still provides information to readers.

Ok, I'll do it.

Quote:Original post by Conner McCloud
Also, rename the class to something else. The last thing you need is a test going haywire because you accidentally used a standard vector instead of your custom one.

I actually defined the class in a different namespace, so there shouldn't be this kind of problem.

This topic is closed to new replies.

Advertisement