C++ Vectors

Arrays -- good and (mostly) bad

An array is used to contain a set of data all which are the same type such as names, ages, or grades.

The good

  1. Arrays can be indexed for easy reference to individual elements.
  2. Dicussion Point -- what else is good about an array?

The bad

  1. The size of the array must be declared at compile time and cannot be changed (without using dynamic memory allocation).
  2. We must use a separate variable to keep track of the number of elements in the array that are actually used.
  3. If we use an array index that's out-of-bounds, our program crashes.
  4. The name of an array is like a pointer which can be very confusing (e.g. when passed to a function).
  5. Elements of an array are not automatically initialized.
  6. We can't assign one array to another unless we write code to do so.
  7. Discussion Point -- what else is bad about an array?

The C++ vector is similar to an array, but also has differences which overcome the "bad". Generally we prefer to use vectors instead of arrays, although there are some programming situations in which an array is required. (E.g. argv, functions from the C-string library such as toupper()). To use a vector, you must include its associated header file -- #include <vector>.

The vector is one of many container provided by the C++ Standard Template Library (STL). The containers provide an interface which allows you to use the container without being concerned with the details.

Vectors are similar to Arrays

  • The statement vector< int > integers(10);
    creates a vector which contains 10 integers and initializes them to zero.
    It's analgous to int integers[10];

    Similarly, the statement vector< string > names(12); creates a vector containing 12 strings all initialized to the empty string.

  • We can also provide an explicit initial value for each element of the vector.
    The statement vector< int> integers (10, -1);
    creates the same vector as above, but all elements are intialized to -1.

    And the statement vector< string > names( 12, "bob"); creates the vector of 12 strings, but initalizes them all to "bob".

  • A vector can be created as a copy of an existing vector. The code vector< string > names (12, "bob"); vector< string > moreNames ( names ); creates the vector moreNames as a copy of the vector names

  • We can access each member of the vector.
    We can use brackets ( [ ] ) just like an array. vector< int > integers(10); integers[3] = 7; for (int i = 0; i < 10; i++) cout << integers[i]; //----------------------------- vector < string > myStrings(5); string mary ("mary"); myStrings[0] = "bob"; myStrings[4] = mary; //----------------------------- vector < Bicycle > bikes(50); Bicycle myBike; bikes[2] = bikes[6] = myBike; However, this method has the same risks as with arrays -- no checking is done to see that the index is valid. If the index is invalid, anything might happen.
    An alternative is to use the vector's at( ) operation. This operation does the same thing as using brackets, so the statements integers[4] = 12; and integers.at( 4 ) = 12; do the same thing, but the at( ) operation checks the index. If the index is out-of-bounds, at( ) "throws an exception" which at present will cause our program to abort immediately, but which we can deal with later.

    The code above is better written as

    vector< int > integers(10); integers.at( 3 ) = 7; // brackets ok in controlled loop for (int i = 0; i < 10; i++) cout << integers[ i ]; //----------------------------- vector < string > myStrings(5); string mary ("mary"); myStrings.at( 0 ) = "bob"; myStrings.at( 4 ) = mary; //----------------------------- vector < Bicycle > bikes(50); Bicycle myBike; bikes.at( 2 ) = bikes.at( 6 ) = myBike;

    Vectors are different from Arrays

  • We can create an empty vector, but not an empty array // an empty vector that will hold ints vector< int > integers;

  • A vector will grow automatically larger as needed when new elements are inserted at the "end" of the vector using the vector's push_back( ) operation. Arrays are fixed size. integers.push_back (5); integers.push_back (4); integers.push_back (3); This initially empty vector now contains 3 elements. As more elements are added using push_back( ), the vector will continue to get larger.

  • The vector has an operation that lets us ask how many elements are in currently stored in the vector. int nrElements = integers.size( ); for (unsigned int i = 0; i < integers.size( ); i++) cout << integers.at( i ) << endl; Note that the size( ) operation returns an unsigned int, and hence the for-loop index must be also be declared as an unsigned int.
  • The vector has an operation that lets us ask how many elements the vector can hold -- aka the vector's "capacity". Don't confuse capacity with size. Note that the vector's capacity will automatically grow as the result of adding elements using push_back. cout << integers.capacity( ) << endl;

  • We can assign one vector to another, just like primitive variable types. Array assignment is not supported in this way. To assign arrays you would need to write a function. vector< int > copyOfIntegers; copyOfIntegers = integers;

  • We can remove a single element of a vector and the vector will automatically shrink and compress the data together // remove integers[i] // make the vector smaller and fill-in the hole integers.erase( integers.begin() + i); In this code, integers.begin( ) is an iterator that is used to "point to" the first element of the vector. We'll discuss iterators later in the course.
  • The capacity of the vector may also be explictly changed using the operations reserve( ) and resize( ). // make the capcity at least 32 elements integers.reserve( 32 ); // set the capcity to 10 more than the current number of elements integers.reserve( integers.size( ) + 10); // change the size to 24 elements integers.resize( 24 );
  • Vectors also support other useful operations empty( ) -- returns true if the vector is empty clear ( ) -- removes all elements from the vector

    Multiple Dimensions

    We can define an array to have (almost) as many dimensions as we need. The elements of the array are then accessed with multiple indices. int scores[ 4 ][ 10 ]; int cube[3][3][3]; scores[3][2] = 99; cube[1][2][0] = -1; With vectors, the approach is a little different. We declare an empty "vector of vector of int". // vector of vector of int vector< vector<int> > scores; // vector of vector of vector of int vector< vector< vector<int> > > cube; Unlike the array declaration above, the vector declaration does not allocate any memory (the vectors are all empty). Your program must dynamically allocate the memory for each vector. We'll show you how to do that a bit later.

    Once the vectors have been allocated, we use multiple indicies to access the elements, just like with arrays.

    Last Modified: Monday, 28-Aug-2006 10:16:07 EDT