I've just finished a homework assignment to implement a simple Vector class.
-Kindly ignore the following two paragraphs if you don't want to read someone whining about their professor
Yadda, yadda, same old stuff. Here's where it gets interesting though; My professor is a bit... strange in his preferences. He's a retired cabinet maker who learned programming in his 50's, and decided that teaching at a local Community College would be a great way to make a decent amount of money without working.
I'm a 'do your own research' kinda guy, and the more I read (here on stackexchange, as well as in various paper books), the more I notice that some of his instructions/requirements are suboptimal to say the least. For example: using namespace std
in every header file he requires us to implement without modifying. Typical feedback on my assignments have been "too many errors to list" and "This code is... odd." I've been getting A's on exams, and solid 70% C's on my assignments. Unfortunately, he doesn't allow us to email him regarding assignments and I'm a full-time student/full-time fast-food worker, so his 5 office-hours per week are really hard to pencil into my schedule.
This is where you kind folks come in. I really, really want to learn how to be a solid programmer. I aspire to one day have header files void of any using namespace
whatsoever. That's why I'd love some input into how I can improve this particular assignment, as well as good practices in general that I can adopt.
-Whining is over, relevant information starts here.
The assignment was to implement a Vector class using the function definitions that my professor provided. Unfortunately, I can't change those and I'm also unable to add any public functions that were not included in his list of definitions. Additionally, I'm not allowed to #include
anything besides <iostream>
.
My Vector.h is as follows:
#ifndef _VECTOR_H_
#define _VECTOR_H_
#include <iostream>
template <class T> class Vector
{
int _index_offset;
unsigned _size;
unsigned _length;
T* _array;
void Double();
void Halve();
public:
Vector (int i = 0);
Vector (const T*, unsigned s, int i = 0);
Vector (const Vector&);
virtual ~Vector();
Vector& operator=(const Vector&);
template <class P>
friend std::ostream& operator<< (std::ostream&, const Vector<T>&);
template <class P>
friend std::ostream& operator<< (std::ostream&, const Vector<T>*);
T& operator[] (int i);
const T& operator[] (int i) const;
Vector operator() (int first, int last) const;
Vector& operator+= (const T&);
Vector& operator+= (const Vector&);
unsigned Length() const;
void Delete (const int i);
void Delete (int first, int last);
void Delete();
void Insert (const T&, int i);
void Insert (const T&);
};
template <class T>
Vector<T>::Vector (int i) : _index_offset(i), _size(0), _length(0)
{
_array = new T[_size];
}
template <class T>
Vector<T>::Vector (const T* first_element, unsigned s, int i) : Vector(i)
{
while (s < _size)
{
Double();
}
for (unsigned i = 0; i < s; i++)
{
_array[i] = first_element[i];
}
}
template <class T>
Vector<T>::Vector (const Vector& other) : _index_offset(other._index_offset), _size(other._size), _length(other._length)
{
_array = new T[_size];
for (unsigned i = 0; i < _length; i++)
{
_array[i] = other._array[i];
}
}
template <class T>
Vector<T>::~Vector()
{
delete[] _array;
}
template <class T>
void Vector<T>::Double()
{
T* new_array;
if (_size == 0)
{
_size = 1;
new_array = new T[1];
delete[] _array;
_array = new_array;
}
else
{
_size *= 2;
new_array = new T[_size];
for (int i = 0; i < _length; i++)
{
new_array[i] = _array[i];
}
delete[] _array;
_array = new_array;
}
}
template <class T>
void Vector<T>::Halve()
{
if (_size >= _length * 2)
{
_size /= 2;
T* new_array = new T[_size];
for (int i = 0; i < _length; i++)
{
new_array[i] = _array[i];
}
delete[] _array;
_array = new_array;
}
}
template <class T>
Vector<T>& Vector<T>::operator= (const Vector& other)
{
if (this != other)
{
_index_offset = other._index_offset;
_size = other._size;
_length = other._length;
delete[] _array;
_array = new T[_size];
for (int i = 0; i < _length; i++)
{
_array[i] = other._array[i];
}
}
return *this;
}
template <class T>
std::ostream& operator<< (std::ostream& os, const Vector<T>& vec)
{
os << "{";
for (int i = 0; i < (vec.Length() - 1); i++)
{
os << vec[i] << ", ";
}
os << vec[(vec.Length() - 1)] << "}";
return os;
}
template <class T>
std::ostream& operator<< (std::ostream& os, const Vector<T>* vec)
{
Vector<T> vec_ref = *vec;
os << vec_ref;
return os;
}
template <class T>
T& Vector<T>::operator[] (int i)
{
int index = i - _index_offset;
return _array[index];
}
template <class T>
const T& Vector<T>::operator[] (int i) const
{
int index = i - _index_offset;
return _array[index];
}
template <class T>
Vector<T> Vector<T>::operator() (int first, int last) const
{
int first_index = first - _index_offset;
int last_index = last - _index_offset;
int new_size = ((last_index - first_index) - 1);
T selection[new_size];
for (int i = 0; i < new_size; i++)
{
selection[i] = _array[first_index + i];
}
return *(new Vector<T>(selection, new_size));
/* TODO: How am I supposed to clean up this Vector? When will Destructor get called?
Will it be destructed as operator() exits, thus returning a pointer to an unreserved location? */
}
template <class T>
Vector<T>& Vector<T>::operator+= (const T& value)
{
if (_size == _length)
{
Double();
}
_array[_length] = value;
_length++;
return *this;
}
template <class T>
Vector<T>& Vector<T>::operator+= (const Vector& vec)
{
for (int i = 0; i < vec._length; i++)
{
*this += vec._array[i];
}
}
template <class T>
unsigned Vector<T>::Length() const
{
return _length;
}
template <class T>
void Vector<T>::Delete (const int i)
{
int index = i - _index_offset;
if (0 <= index && index < _length)
{
_length--;
for (int i = index; i < _length; i++)
{
_array[i] = _array[i+1];
}
if (_size >= _length * 2)
{
Halve();
}
} else std::cerr << "Error - Vector<T>::Delete(const int i): i cannot be less than 0, or greater than or equal to Vector._length" << std::endl;
}
template <class T>
void Vector<T>::Delete (int first, int last)
{
int first_index = first - _index_offset;
int last_index = last - _index_offset;
if ((first_index >= 0 && last_index < Length()) && (first_index < last_index))
{
for (int num_deleted = 0; num_deleted < (last_index - first_index); num_deleted++)
{
Delete(first_index);
}
} else std::cerr << "Error - Vector<T>::Delete(int first, int last): Invalid arguments." << std::endl;
}
template <class T>
void Vector<T>::Delete()
{
Delete(0);
}
template <class T>
void Vector<T>::Insert(const T& value, int i)
{
int index = i - _index_offset;
if (index < _length)
{
if (_length == _size)
{
Double();
}
for (int j = _length; j > index; j--)
{
_array[j] = _array[j - 1];
}
_array[index] = value;
_length++;
} else std::cerr << "Error - Vector<T>::Insert(const T& value, int i): i cannot be greater than or equal to Vector._length" << std::endl;
}
template <class T>
void Vector<T>::Insert(const T& value)
{
Insert(value, _index_offset);
}
#endif
Some explanation: The assignment requires that the user be able to specify a starting index for the Vector besides 0 if they so choose. For example, if they declare Vector<int> vec = new Vector<int>(10)
, the initial element of the Vector will be at vec[10]
, and vec[9]
or anything lower would be out of bounds. He said this would be one of the greatest challenges of the assignment, but the _index_offset
solution seemed pretty obvious to me so I'm concerned that it's a terrible solution. _size
and _length
, if it's not obvious, refer to the current reserved memory of the array and its number of occupied elements, respectively.
My professor's unhelpful (and to my sensitive heart, frankly hurtful) feedback has given me a burning, vindictive desire to submit an assignment that is unimpeachable in its implementation. I'm highly confident that my current implementation could do with a huge amount of improvement, and I'd be extremely grateful for any suggestions you fine folks could offer.
_size
always be 0? Aren't they always allocating an array of 0 elements to start with? And so isn't thewhile
loop in the 2nd one unnecessary since it will never execute? \$\endgroup\$