This is a follow up from a post I made here. I am creating a generic vector class. I have changed the code around considerably since I had so many errors (decided to just re-do it from scratch).
I just want to check to see if there is additional errors I made or certain parts that I can improve or add upon this class I made. Following the three/zero/five rule, I may need to add a move constructor to my class, but I just want to first see what you all think.
Here is my header file:
#ifndef Vector_h
#define Vector_h
#include <initializer_list>
#include <iostream>
template <class T>
class Vector {
private:
static const int INITIAL_CAPACITY = 100;
// Instance variables
int capacity; // capacity of vector
int size;
T* data;
// Method: deepCopy
void deepCopy(const Vector<T> & source) {
capacity = source.size + INITIAL_CAPACITY;
this->data = new T[capacity];
for (int i = 0; i < source.size; i++) {
data[i] = source.data[i];
}
size = source.size;
}
// Method: expandCapacity
void expandCapacity() {
T *oldData = data;
capacity *= 2;
data = new T[capacity];
for (int i = 0; i < size; i++) {
data[i] = oldData[i];
}
delete[] oldData;
}
public:
// Constructors
Vector(); // empty constructor
Vector(int n, const T& value); // constructor
Vector(Vector const& vec); // copy constructor
~Vector(); // destructor
// Overloading operators
T& operator[](int index);
bool operator==(Vector&) const;
Vector<T>& operator=(Vector<T> const& rhs);
friend Vector<T>& operator+(Vector<T>& source1, Vector<T>& source2) {
int n = source1.getSize() + source2.getSize();
static Vector<T> newSource(n, 0);
for (int i = 0; i < source1.getSize(); i++) {
newSource[i] = source1[i];
}
for (int i = 0; i < source2.getSize(); i++) {
newSource[i + source1.getSize()] = source2[i];
}
return newSource;
}
friend std::ostream& operator<<(std::ostream& str, Vector<T>& data) {
data.display(str);
return str;
}
// Member functions
void display(std::ostream& str) const;
int getSize() const;
int getCapacity() const;
bool isEmpty() const;
void clear();
T get(int index) const;
void set(int index, const T& value);
void set(int index, T&& value);
void insert(int index, const T& value);
void insert(int index, T&& value);
void remove(int index);
void push_back(const T& value);
void pop_back();
};
template <class T>
Vector<T>::Vector() {
capacity = INITIAL_CAPACITY;
size = 0;
data = new T(capacity);
}
template <class T>
Vector<T>::Vector(int n, const T& value) {
capacity = (n > INITIAL_CAPACITY) ? n : INITIAL_CAPACITY;
data = new T[capacity];
size = n;
for (int i = 0; i < n; i++) {
data[i] = value;
}
}
template <class T>
Vector<T>::Vector(Vector const& vec) {
deepCopy(vec);
}
template <class T>
Vector<T>::~Vector() {
delete[] data;
}
template <class T>
T& Vector<T>::operator[](int index) {
if(index < 0 || index >= size) {
throw std::invalid_argument ("[]: index out of range");
}
return data[index];
}
template <class T>
bool Vector<T>::operator==(Vector& rhs) const {
if(getSize() != rhs.getSize()) {
return false;
}
for(int i = 0; i < getSize(); i++) {
if(data[i] != rhs[i]) {
return false;
}
}
return true;
}
template <class T>
Vector<T>& Vector<T>::operator=(const Vector<T> &rhs) {
if(this != &rhs) {
delete[] data;
deepCopy(rhs);
}
return *this;
}
template <class T>
void Vector<T>::display(std::ostream& str) const {
for(int i = 0; i < size; i++) {
str << data[i] << "\t";
}
str << "\n";
}
template <class T>
int Vector<T>::getSize() const {
return size;
}
template <class T>
int Vector<T>::getCapacity() const {
return capacity;
}
template <class T>
bool Vector<T>::isEmpty() const {
return (size == 0);
}
template <class T>
void Vector<T>::clear() {
size = 0;
}
template <class T>
T Vector<T>::get(int index) const {
if(index < 0 || index >= size) {
throw std::invalid_argument ("get: index out of range");
}
return data[index];
}
template <class T>
void Vector<T>::set(int index, const T& value) {
if(index < 0 || index >= size) {
throw std::invalid_argument ("set: index out of range");
}
data[index] = value;
}
template <class T>
void Vector<T>::set(int index, T&& value) {
if(index < 0 || index >= size) {
throw std::invalid_argument ("set: index out of range");
}
data[index] = std::move(value);
}
template <class T>
void Vector<T>::insert(int index, const T& value) {
if(size == capacity) {
expandCapacity();
}
// if(index < 0 || index >= size) { // commented this out because I was getting a sigbart1 error that I could not fix
// throw std::invalid_argument ("insert: index out of range");
// }
//
for(int i = size; i > index; i--) {
data[i] = data[i-1];
}
data[index] = value;
size++;
}
template <class T>
void Vector<T>::insert(int index, T&& value) {
if(size == capacity) {
expandCapacity();
}
if(index < 0 || index >= size) {
throw std::invalid_argument ("insert: index out of range");
}
for(int i = size; i > index; i--) {
data[i] = data[i-1];
}
data[index] = std::move(value);
size++;
}
template <class T>
void Vector<T>::remove(int index) {
if(index < 0 || index >= size) {
throw std::invalid_argument ("insert: index out of range");
}
for(int i = index; i < size - 1; i++) {
data[i] = data[i+1];
}
size--;
}
template<class T>
void Vector<T>::push_back(const T& value) {
insert(size, value);
}
template<class T>
void Vector<T>::pop_back() {
remove(size - 1);
}
#endif /* Vector_h */
Here is the main.cpp file that tests this class:
#include <cassert>
#include <iostream>
#include "Vector.h"
int main(int argc, const char * argv[]) {
///////////////////////////////////////////////////////////////////////
///////////////////////////// VECTOR //////////////////////////////////
///////////////////////////////////////////////////////////////////////
Vector<int> nullVector; // Declare an empty Vector
assert(nullVector.getSize() == 0); // Make sure its size is 0
assert(nullVector.isEmpty()); // Make sure the vector is empty
assert(nullVector.getCapacity() == 100); // Make sure its capacity is greater than 0
Vector<int> source(20, 0); // Declare a 20-element zero Vector
assert(source.getSize() == 20); // Make sure its size is 20
for (int i = 0; i < source.getSize(); i++) {
source.set(i, i);
assert(source.get(i) == i); // Make sure the i-th element has value i
}
source.remove(15); // Remove the 15th element
assert(source[15] == 16); // Make sure the 15th element has value 16
source.insert(15, 15); // Insert value 15 at the index 15
assert(source[15] == 15); // Make sure the 15th element has value 15
source.pop_back(); // Remove the last element
assert(source.getSize() == 19); // Make sure its size is 19
source.push_back(19); // Insert value 20 at the bottom
assert(source.getSize() == 20); // Make sure its size is 20
assert(source.get(19) == 19); // Make sure the 19th element has value 19
Vector<int> copyVector(source); // Declare a Vector equal to source
for (int i = 0; i < source.getSize(); i++) {
assert(copyVector[i] == source[i]); // Make sure copyVector equal to source
}
// Vector<int> newSource = copyVector + source;
std::cout << "source: \n"<< source; // Print out source
std::cout << "copyVector: \n" << copyVector; // Print out copyVector
Vector<int> newSource = source + copyVector; // Concatenate source and copyVector
std::cout << "newSource: \n" << newSource; // Print out source + copyVector
source.clear(); // Clear source
assert(source.getSize() == 0); // Make sure its size is 0
std::cout << "Vector unit test succeeded." << std::endl;
return 0;
}