Skip to main content
GameDev.net gamedev.net
🔒 Locked

[SOLVED]Array template

Started by thallish Jan 25, 2006 at 7:06 AM 8 replies 700+ views
Original Post
thallish
thallish
Hi I've have been playing around with an array template class and I've run into something that I wuold like to get clerance on. First of all the problem arises when the destructor is checking for existance of the member array I'm using: This works: ~Array() { if(m_aArray == NULL) { delete[] m_aArray; } m_aArray = NULL; } This doesn't: ~Array() { if(m_aArray != NULL) { delete[] m_aArray; } m_aArray = NULL; } Now isn't it more logical to check if the array does exist and then delete it other than delete it only if it is doesn't exist? I would think so I'm pretty convinced that the snippet that doesn't work is semantically the right way but is it done correctly syntactically? I'm using Visual C++ 2005 Express and the debugger breaks at the line that says delete[] m_aArray. Can't figure this out. Hope that somebody can;-) regards thallish PS. Just throwing the entire class here. Maybe something else is wrong:

template <class T>
class Array
{
public:

	// Constructor: Construct a new array of a type T with specified size
	Array (int size)
	{
		m_aArray = new T[size];
		m_iSize = size;
	}

	// Destructor: If array exists delete it 
	// otherwise just set pointer to NULL
	~Array()
	{
		if(m_aArray == NULL)
		{
			delete[] m_aArray;
		}
		m_aArray = NULL;
	}

	// Returns the current size of the array
	int GetSize()
	{
		return m_iSize;
	}

	// If index is not inside bounds, return false
	// If index is inside bounds, add item to array at index and return true
	bool Add(T item, int index)
	{
		if(index < 0)
		{
			// Illegal index!
			return false;
		}

		// If larger then current size
		// resize array to size specified in index
		if(index > m_iSize - 1)
		{
			if(!this->ReSize(index))
				return false
		}
				
		m_aArray[index] = item;
		return true;
	}

	// Resize array to double the current size
	bool DoubleSize()
	{
		// Declaring variables
		int new_size;
		T* new_array;

		// Setting new size of array
		new_size = this->GetSize();
		
		// If size of array is less or equal to 0
		// size is set to 1
		if(new_size <= 0)
		{
			new_size = 1;
		}
		
		// Doubling size
		new_size = new_size * 2;

		// Setting size of tempoary array to the new size
		new_array = new T[new_size];

		if(new_array == NULL)
			return false;

		// Transfering all elements from old array to new array
		for(int i = 0; i < m_iSize; i++)
		{
			new_array = m_aArray;
		}
		
		// Setting the new values
		m_aArray = new_array;
		m_iSize = new_size;

		// If the tempoary array exists delete it
		if(new_array != NULL)
			delete[] new_array;
		
		new_array = NULL;
		
		return true;
	}

	// Resizes array to specified size
	bool ReSize(int new_size)
	{
		// If illegal argument is given return false
		if(new_size < 0)
		{
			return false;
		}
		
		// Setting size of temporary array to specified size
	    T* new_array = new T[new_size];
		
		if(new_array == NULL)
			return false;
		
		int min;

		// Determine which array is smallest
		if(m_iSize < new_size)
		{
			min = m_iSize;
		}
		else
		{
			min = new_size;
		}	

		// Transfering items from old to temporary array
		for(int i = 0; i < min; i++)
		{
			new_array = m_aArray;
		}

		// Setting new values
		m_aArray = new_array;
		m_iSize = new_size;

		// If temporary array exist delete it
		if(new_array != NULL)
			delete[] new_array;
		
		new_array = NULL;

		return true;
	}

	T& operator[] (int index)
	{
		return m_aArray[index];
	}

private:
	T*	m_aArray;
	int m_iSize;
};



And this is my main

#include <iostream>
using namespace std;
#include "array_template.h"

int main()
{
	int size = 5;
	Array<float> here(size); 

	cout << here.GetSize() << endl;
	here.ReSize(19);
	cout << here.GetSize() << endl;
	here.DoubleSize();
	cout << here.GetSize() << endl;
	int a;
	cin >> a;
	
	if(a)
	 return 0;
}


[Edited by - thallish on January 25, 2006 12:45:28 PM]
regards/thallishI don't care if I'm known, I'd rather people know me
playmesumch00ns
playmesumch00ns
Haven't checked through your code in too much detail, but yeah I'd say something is screwed.

More to the point, C++ requires that delete'ing a null pointer is allowed, so there's no need for that if in your destructor just do.

~Array(){    delete[] m_aArray;}


Since when the destructor exits, the pointer itself won't exist, there's no point settng it to null either.
playmesumch00ns
playmesumch00ns
You problem is this code:

                // Setting size of tempoary array to the new size		new_array = new T[new_size];		if(new_array == NULL)			return false;		// Transfering all elements from old array to new array		for(int i = 0; i < m_iSize; i++)		{			new_array = m_aArray;		}				// Setting the new values		m_aArray = new_array;		m_iSize = new_size;		// If the tempoary array exists delete it		if(new_array != NULL)			delete[] new_array;				new_array = NULL;


Should be more like this:

// Make a new arraynew_array = new T[size];// Copy the existing array to the new datamemcpy( new_array, m_aArray, sizeof( T ) * m_iSize );// Delete the old arraydelete[] m_aArray;// Set the member array to be the new arraym_aArray = new_array;
bakery2k1
bakery2k1
I'll go through your
bool ReSize(int new_size)

function. First of all, is there any reason why you have separate ReSize and DoubleSize functions? Couldn't DoubleSize simply call ReSize(2 * currentSize)?

Here you create a new array:
T* new_array = new T[new_size];		if(new_array == NULL)	return false;


Note that new[] will never return NULL. The allocation will either succeed, or it will throw an exception.

Then, after copying values, you do:
// Setting new valuesm_aArray = new_array;m_iSize = new_size;// If temporary array exist delete itif(new_array != NULL)delete[] new_array;


After copying values to new_array, you delete it! I think you wanted:

delete[] m_aArray;m_aArray = new_array;m_iSize = new_size;


Noting that it is perfectly safe to delete[] a NULL pointer.
joanusdmentia
joanusdmentia
Deleting a null pointer is not an error, it is defined in the standard as doing nothing. Furthermore, setting m_aArray to NULL in the destructor is pointless, m_aArray will cease to exist when the destructor exists so you could never accidently delete it again.

So, all you really want in the destructor is
~Array(){    delete[] m_aArray;}


Your getting an exception on the line with delete[] because your DoubleSize() and ReSize() functions are broken. They both create a new array, fill it out, assign it to the m_aArray variable (you loose the pointer original array and have a memory leak), and then delete the new array (leaving a pointer to deleted memory in m_aArray, hence the exception in the destructor for trying to delete already deleted memory).


BTW, unless this is a learning exercise you should be using std::vector instead of reinventing the wheel.


EDIT: God damn it!! Beaten not once, not twice but three times!!!
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The nly verdict is vengeance; a vendett o
MaulingMonkey
MaulingMonkey
Point #1: delete is allowed on null pointers - it does nothing however.
Point #2: The "working" destructor is guaranteed to do nothing useful.
Point #3: The "not working" destructor does not work because something is screwed up elsewhere - it is sane, although noisy - just a "delete[] m_aArray" will do fine.
Point #4: Your code has a lot of noise. People know what a variable definition looks like. There's some semantic junk in there too:

new_array = new T[new_size];if(new_array == NULL)    return false;


new does not return NULL. If it fails to allocate it throws an exception. Ergo, the if statement will never execute. There is a "nothrow" variant of new, but it must be explicitly used to achieve this behavior - google it for more info if desired.

		// Setting the new values		m_aArray = new_array;		m_iSize = new_size;		// If the tempoary array exists delete it		if(new_array != NULL)			delete[] new_array;


Another mistake. First you overwrite the pointer to your old array stored in m_aArray (a memory leak!), and then you delete the array you just allocated and copied a whole bunch of values to. This is obviously a mistake! This should be rewritten as:

delete[] m_aArray;m_aArray = new_array;


Similar problems to these as found in DoubleSize are also found in Resize, if not the very exact thing. This is symptomatic of not reusing code - DoubleSize and Resize do the exact same thing, resize the array!!! So why is their code not shared? DoubleSize could be rewritten as:

bool DoubleSize() {    return ReSize( 2 * GetSize() );}


A lot smarter than copy/pasting code into 2389476529385476895 different places.

Quote:
Original post by joanusdmentia
EDIT: God damn it!! Beaten not once, not twice but three times!!!


4 here, deal with it :-p.
thallish
thallish
Hi

Thx for the many quick replies. This is a learning experience so i might as well get some bugs ;-) (I usually use std::vector but wanted to do some background digging)

I'll have to read up on so array stuff and then I''ll revise the code and post it again(edit it maybe).

regards
thallish

EDIT: Oh yeah, about the ReSize/DoubleSize thing first wrote the DoubleSize and then figured out it would be smarter to have the other;-). MaulingMonkey you're of course right that the other thing is smarter
regards/thallishI don't care if I'm known, I'd rather people know me
snk_kid
snk_kid
Quote:
Original post by thallish
Thx for the many quick replies. This is a learning experience so i might as well get some bugs ;-) (I usually use std::vector but wanted to do some background digging)


Don't want to sound like an ass or put you down so don't take this the wrong way but writing your own custom array type does not mean you will learn how std::vector is implementated and this is seems to be the exact case as from briefly looking at your code. std::vector is not implementated like that, in order to understand how it is typically implementated you need to know some low-level, non-typical, non every day advance C++ code (and that is the whole reason for it's existance!).

If you really want to know how it typically works you would do better with your time by actually looking at/studying a modern C++ compiler's implementation of std::vector & std::allocator (the default allocator type used by standard lib containers), GCC is one good example where you can see virtually all of the source code for it.
thallish
thallish
Ok to clarify, then i am not talking about how std::vector is implemented but more of how array's are working and what I can do with them, or more specifically what i don't know about them

regards
/thallish
regards/thallishI don't care if I'm known, I'd rather people know me
thallish
thallish
Ok now i got it all working

#pragma warning(disable: 4305)template <class T>class Array{public:	// Constructor: Construct a new array of a type T with specified size	Array (int size)	{		m_aArray = new T[size];		m_iSize = size;	}	// Destructor: Delete member array	~Array()	{		delete[] m_aArray;	}	// Returns the current size of the array	int GetSize()	{		return m_iSize;	}	// If index is not inside bounds, return false	// If index is inside bounds, add item to array at index and return true	bool Add(T item, int index)	{		if(index < 0)		{			// Illegal index!			return false;		}		// If larger then current size		// resize array to size specified in index		if(index > m_iSize - 1)		{			if(!this->ReSize(index))				return false;		}						m_aArray[index] = item;		return true;	}	// Adding element in last position	bool PushBack(T item)	{		int iOldSize = this->GetSize();				// Resize by 1		if(!this->ReSize(iOldSize + 1))			return false;				// Adding at end		if(!this->Add(item, iOldSize))			return false;				return true;	}	// Inserting element at specified position	// while keeping	bool Insert(T item, int index)	{		int iOldSize = this->GetSize();		// Resizing by one		if(!this->ReSize(iOldSize + 1))			return false;				T* new_array = new T[this->GetSize()];		// The untouched elements		for(int i = 0; i < index; i++)			new_array = m_aArray;		// The elements that shift		for(int i = index ; i < this->GetSize(); i++)			new_array = m_aArray[i-1];		// Specified element to insert		new_array[index] = item;				// Adding the array to member array		delete[] m_aArray;		m_aArray = new_array;		return true;	}	// Resize array to double the current size	bool DoubleSize()	{		int doubleSize = 2 * this->GetSize(); 				this->ReSize(doubleSize);		return true;	}	// Resizes array to specified size	bool ReSize(int new_size)	{		// If illegal argument is given return false		if(new_size < 0)		{			return false;		}				// Setting size of temporary array to specified size	    T* new_array = new T[new_size];				int iMinSize;		// Determine which array is smallest		if(m_iSize < new_size)		{			iMinSize = m_iSize;		}		else		{			iMinSize = new_size;		}			for(int i = 0; i < iMinSize; i++)			new_array = m_aArray;		if(m_aArray != 0)			delete[] m_aArray;				// Setting the new values		m_aArray = new_array;		m_iSize = new_size;				return true; 	}	// Returning element at specified index 	T& operator[] (int index)	{		return m_aArray[index];	}private:			T*	m_aArray;	int m_iSize;};


regards
/thallish

EDIT: Added insert and pushback methods

[Edited by - thallish on January 25, 2006 1:44:05 PM]
regards/thallishI don't care if I'm known, I'd rather people know me

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.