Let’s start by looking at a dynamically allocated array. The code below starts with an array capable of holding just 2 elements. As it adds elements and reaches the end of the array, it creates a new array twice the size of the previous array and copies the old array to the new before deleting the old array. This works well, but in practice there are a couple of things that are easy to mess up. Forgetting to delete or copy is easy to do in a larger project, and not always easy to debug.
#include <iostream> using namespace std; void showArray(int arr[], int arrayCount) { cout << "Array values:\n"; for(int i = 0; i < arrayCount; i++) { cout << arr[i] << endl; } cout << endl; } int main(int argc, const char * argv[]) { int arraySize = 2, *myArray; myArray = new int[arraySize]; // Start out with an array of 2 uninitialized elements for(int i = 0; i < 11; i++) { if(i == arraySize) { // Need more space? int *temp = new int[2*arraySize]; // Double the space memcpy(temp,myArray,arraySize * sizeof(int)); // Copy to new, expanded space delete myArray; // delete the old myArray = temp; // set the pointer to new space arraySize *= 2; // set array size to double its old size } myArray[i] = i*10; // initialize the ith value to i*10 showArray(myArray, i+1); // Array will have i+1 elements since we're starting from 0 } cout << "\nSame thing using a 'normal' array:\n"; int normalArray[11]; for(int i = 0; i < 11; i++) { normalArray[i] = i; cout << "i = " << i << "\n"; showArray(normalArray, i+1); } return 0; }
Now let’s look at a class that manages the dynamic array for us. The code below allows us to “push” elements onto an internally managed dynamic array. Using it, we don’t need to worry about deleting or expanding our array. The class does it for us! There are still some limitations. For example, the array must be of the type declared in the class (here it’s int) so we’d need many versions of the same class to handle doubles, strings, or other classes.
#include <iostream> using namespace std; class arrayClass { int mysize; int maxsize; int *myArray; public: arrayClass() : maxsize(2) { myArray = new int[maxsize]; // Start out with an array of 2 uninitialized elements mysize = 0; // We haven't initialized either element } ~arrayClass() { delete [] myArray; } void push_back(int num) { // Need more space? if(mysize < maxsize) { // No, so assign value and increment index myArray[mysize] = num; mysize++; } else { // Yes, so int *tmp = new int[maxsize*2]; // Double the space memcpy(tmp, myArray, sizeof(int)*maxsize); // Copy to new, expanded space delete myArray; // delete the old myArray = tmp; // set the pointer to new space maxsize *= 2; // set array size to double its old size myArray[mysize] = num; // initialize the ith value to num mysize++; // increment the index } } int operator [](int n) { return myArray[n]; } void showArray() { cout << "Size of array: " << mysize << " maxsize: " << maxsize << endl; cout << "Array values:\n"; for(int i = 0; i < mysize; i++) { cout << myArray[i] << endl; } cout << endl; } }; int main(int argc, const char * argv[]) { arrayClass sample; for(int i = 0; i < 11; i++) { sample.push_back(i*10); // Add 10*i to the end of the dynamic array cout << "i = " << i << endl; sample.showArray(); } return 0; }
A more complete approach it to use C++ templates. The code below handles arrays of strings, integers, doubles, and classes like the one (apoint) provided. From here, I hope it’s clear how the rest of the vector class would evolve into what it is today.
#include <iostream> using namespace std; template <class T> class vectorSimulator { int mysize; int maxsize; T *myarray; public: vectorSimulator() { mysize = 0; maxsize = 2; myarray = new T[maxsize]; } vectorSimulator(vectorSimulator<T> &orig) { mysize = orig.mysize; maxsize = orig.maxsize; myarray = orig.myarray; } ~vectorSimulator() { delete [] myarray; } void push_back(T &item) { if (mysize < maxsize) { myarray[mysize] = item; mysize++; } else { T *newarray; newarray = new T[maxsize*2]; memcpy(newarray, myarray, maxsize*sizeof(T)); delete [] myarray; myarray = newarray; maxsize *=2; myarray[mysize]=item; mysize++; } } void erase(int n) { if(n < mysize && n >= 0) { for (int i = n; i < mysize-1; i++) { myarray[i] = myarray[i+1]; } mysize--; } } void erase(int n, int m) { if(n+m < mysize && n >= 0 && m > 0) { for (int i = n; i <= n + m; i++) { myarray[i] = myarray[i+m]; } mysize = mysize - m; } } int vectorsize() { return mysize; } T operator [](int n) { return myarray[n]; } }; template <class T2> void show(vectorSimulator<T2> &vec) { for (int i = 0; i < vec.vectorsize(); i++) { cout << i << ": " << vec[i] << endl; } } template <typename T3> void show_special(vectorSimulator<T3> &vec) { for (int i = 0; i < vec.vectorsize(); i++) { cout << i << ": " << vec[i].print() << endl; } } class apoint { double x,y; public: apoint() {} apoint(double a, double b ) {x=a;y=b;} string print() { char p[50]; sprintf(p, "%1.2f, %1.2f", x, y); return p; } }; int main() { vectorSimulator<string> test_strings; vectorSimulator<int> test_integers; vectorSimulator<double> test_doublefloats; vectorSimulator<apoint> point_tests; string test_string[10] = {"One","two","three","four","five","six","seven","eight","nine","ten"}; int test_ints[10] = {1,2,3,4,5,6,7,8,9,10}; double test_doubles[10] = {.1,.2,.3,.4,.5,.6,.7,.8,.9,1.0}; string temp; int index; for (int i = 0; i < 33; i++) { index = i % 10; test_strings.push_back(test_string[index]); } for (int i = 0; i < 10; i++) { test_integers.push_back(test_ints[i]); } for (int i = 0; i < 10; i++) { test_doublefloats.push_back(test_doubles[i]); } for (int i = 0; i < 6; i++) { apoint temp(test_ints[i],test_doubles[i]); point_tests.push_back(temp); } cout << "Strings\n"; show(test_strings); test_strings.erase(1,2); cout << endl << "test erase\n"; show(test_strings); cout << endl << "Integers\n"; show(test_integers); cout << endl << "Doubles\n"; show(test_doublefloats); cout << endl << "Points\n"; show_special(point_tests); return 0; }