Array exception

A. First Edition
This is second edition of my simple assignment which requires you to write both template and exception handling.
B.The problem
How to use template for re-usage and exception handling mechnism?
 
 
C.The idea of program
What should I say about this?
D.The major functions
E.Further improvement
1. I should write one more user-defined class object for testing.
 
//file Array.h
///////////////////////////////////
// Date: August 10, 2003
// Author: C. Taillefer
// File: Array.h
///////////////////////////////////
// Array Header File with patial
// implementation.
///////////////////////////////////


#ifndef ARRAY_H
#define ARRAY_H

#include <iostream>

using namespace std;

#include "exception.h"

template < class T >
class Array 
{
   friend ostream &operator<<( ostream & output, const Array<T>& a )
   {
		for ( int i = 0; i < a.size; i++ ) 
		{
			output << ' ' << a.ptr[ i ];	
		}
		return output ; 
   }		
public:
   Array( int = 10 );                   // default constructor
   Array( const Array & );              // copy constructor
   ~Array();                            // destructor
   int getSize() const;                 // return size
   T& operator[]( int index ) ;
   bool operator==( const Array& ) const ;
   const Array& operator() ( int first, int last ) ;
private:
   int size; 				// size of the array
   T* ptr; 					// pointer to first element of array
};



//////////////////////////////////////////////////////////////////
// Default constructor for class Array (default size 10)
template < class T >
Array< T >::Array( int arraySize ) {
	if (arraySize <= 0 ) { throw Error( "Invalid Size" ) ; }
	else {
		size = arraySize ; 
		ptr = new T[ size ]; // create space for array
		if ( ptr == 0 ) { throw Error( "No Memory Allocation" ) ; }    
	
		for ( int i = 0; i < size; i++ )
			ptr[ i ] = 0;          // initialize array
	}
	
}


////////////////////////////////////////////////////////////////
// Copy constructor for class Array
// must receive a reference to prevent infinite recursion
template < class T >
Array< T >::Array( const Array &init ) : size( init.size ) {
	ptr = new T[ size ]; // create space for array
	if ( ptr == 0 ) { throw Error( "No Memory Allocation" ) ; }  

	for ( int i = 0; i < size; i++ )
		ptr[ i ] = init.ptr[ i ];  // copy init into object
}


////////////////////////////////////////////////////////////////
// Destructor for class Array
template < class T >
Array< T >::~Array() {
   delete [] ptr;            // reclaim space for array
}


////////////////////////////////////////////////////////////////
// Get the size of the array
template < class T >
int Array< T >::getSize() const { return size; }


////////////////////////////////////////////////////////////////
// Overloaded subscript operator for class Array 
template < class T >
T& Array< T >::operator[]( int index ) {
	if (index >= size ) { throw Error( "Index Out of Bounds" ) ; }
	return ptr[ index ] ;	
}



//if user pass a "class T", say "Student" which is a user-defined class,
//then user must define operator overloading function:
// bool Student::operator==(Student& S);
template< class T >
bool Array<T>::operator ==(const Array<T>& other) const
{
	if (this==&other)//test if this object
	{
		return true;
	}
	else
	{
		if (size==other.size)//size must be same
		{
			for (int i=0; i<size; i++)
			{
				//I guess for a certain class "T", user usually only defines "=="
				//instead of "!="
				if (!(ptr[i]==other.ptr[i]))//should user define this by himself?
				{
					return false;
				}
			}
			return true;//this means all element is equal
		}
	}
	return false;
}
//return object itself with chopped off array
template< class T >
const Array<T>& Array<T>::operator()(int first, int last)
{
	if (first<0||last<0)
	{
		throw Error("Negative subscript not allowed!");
	}
	if (last<first)
	{
		throw Error("Last cannot be smaller than first!");
	}
	if (first>=size||last>=size)
	{
		throw Error("Index Out of Bounds");
	}
	if (first==0&&last==size-1)//this is a shortcut which returns original itself
	{
		return *this;
	}
	//use safe mode to backup old ptr and size, in case exception, try restore them
	T* oldPtr = ptr;
	int oldSize = size;
	size = last - first + 1;
	ptr = new T [size];
	if (ptr==NULL)
	{
		//before throw, restore old data first
		ptr= oldPtr;
		size = oldSize;
		throw Error("No Memory Allocation");
	}
	for (int i=0; i<size; i++)
	{
		ptr[i] = oldPtr[first+i];
	}
	delete [] oldPtr;//delete old ptr

	return *this;
}

#endif
//file Exception.h
///////////////////////////////////
// Date: August 10, 2003
// Author: C. Taillefer
// File: exception.h
///////////////////////////////////
// Exception Object Header File 
// with implementation.
///////////////////////////////////

#ifndef EXCEPTION_H
#define EXCEPTION_H

#include <cstring>

class Error {
public:
	Error( char* msg = "" ) 
	{ message = new char[ strlen(msg)+1 ] ; strcpy( message, msg ) ; }
	Error( const Error& err ) 
	{ message = new char[ strlen(err.message)+1 ] ; 
		strcpy( message, err.message ) ; }
	~Error() { delete [] message ; }
	const char* what() { return message ; }
private:
	char* message ;
} ;


#endif
 
//file Driver.cpp
///////////////////////////////////
// Date: August 13, 2003
// Author: Qingzhe Huang
// File: Driver.cpp
///////////////////////////////////
// Driver .cpp file and major feature is:
// 1.  I don't like copy&paste same code, so I made a template function generalTest()
//     which takes the array object with parameter and also its type. Therefore, I can
//	   test all type of template array with this templated function.
// 2.  Originally I also want to test array with object, say a user-defined class "Student",
//	   but it takes more time. (this class must define "operator==" as my array need this in
//	   its member function, so does "assign operator=".	
// 
///////////////////////////////////

#include <iostream>
#include "Array.h"
#include "Exception.h"

using namespace std;

//this template function can test all data type provided
//user pass a data array with same data type of the array class
template<class T>
void generalTest(Array<T>& A, T* dataArray);

int main()
{
	char* strArray[6]={"str1","str2","str3","str4","str5","str6"};
	int integerArray[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
		
	Array<int> intArray(12);
	Array<char*> charArray(6);
	
	//test int type with int data array
	generalTest<int>(intArray, integerArray);

	generalTest<char*>(charArray, strArray);

	return 0;
}

//this saves those copy&paste garbage code as you don't need to write
//same code to test every data type.
template<class T>
void generalTest(Array<T>& A, T* dataArray)
{
	cout<<"test constructor by assign negative size\n";
	try
	{
		Array<T> temp(-1);
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}

	cout<<"test friend function and operator[]"<<endl;
	for (int i=0; i<A.getSize(); i++)
	{
		A[i] = dataArray[i];
	}
	cout<<A<<endl;

	cout<<"Here goes the try block, see if ""out of bounds"" error can be caught"
		<<" by access one more than size\n";
	try
	{
		cout<<"The size of object is "<<A.getSize()<<endl;
		cout<<"try to access one more than size"<<endl;
		cout<<A[A.getSize()+1];
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}
	cout<<"now test operator== by compare object itself\n";
	cout<<"A==A should be: "<<(A==A?"true":"false")<<endl;
	cout<<"now test operator == by compare object with different size\n";
	Array<T> B(A.getSize()-1);

	for (i=0; i<B.getSize(); i++)
	{
		B[i] = dataArray[i];
	}
	cout<<"A, B with different size,so (A==B) should be: "<<(A==B?"true":"false")<<endl;
	cout<<"now test copy constructor\n";
	Array<T> C(A);

	cout<<"C is created by initialized by A, so C==A should be: "
		<<(C==A?"true":"false")<<endl;

	cout<<"now try to catch No Memory Allocated by allocating too much memory\n";

	try
	{
		Array<T> D(999999999);
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}
	cout<<"Now try substring function by passing nagative subscript\n";
	try
	{
		cout<<A(-1, A.getSize() -2);
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}
	cout<<"try to test last is smaller than first\n";
	try
	{
		cout<<A(A.getSize()-4, A.getSize()-5);
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}
	cout<<"try to get correct substring\n";
	try 
	{
		cout<<"original array is like this\n"<<A<<endl;
		cout<<"now substring is like this\n"<<A(A.getSize()-5, A.getSize()-3)<<endl;
	}
	catch(Error E)
	{
		cout<<E.what()<<endl;
	}
}


	





Running result of program:

 
	

			


                                 back.gif (341 bytes)       up.gif (335 bytes)         next.gif (337 bytes)