computer science II
c m s c 214  
f a l l   2 0 0 1  

STL vectors

© by Charles Lin. All rights reserved. You must receive written permission from Charles Lin to reproduce this webpage in any form.

Many of you have never seen templates before. Those of you who took the AP exam may have, since some simple version of templates are now assumed for the exam.

Fortunately, you don't have to understand templates very well to use them, and that's one of the goals of the first project: to use templates.

In particular, we're going to use an STL vector. STL stands for "Standard Template Library". The idea for this library is probably about 10 years old. It was incorporated into the C++ standard in 1998.

The goal was to produce container classes to store a bunch of objects of similar types. You've seen some already. Whenever you make an array of objects, you are, effectively, creating a container of such objects.

However, arrays are rather rigid. If you want to resize the array, you have to do dynamic allocation, and while the code isn't difficult, you still have to write code.

STL gives you classes that can hold objects of any type. All you have to do is figure out what operations you want, and how efficient you want those operations to be, and find the appropriate container class.

A vector is basically a dynamic array, which can be resized as needed. Let's look at some basics of vectors.

Declaring vectors

You can declare a vector (which is like a 1-D array) of any type. They can be pointers, basic types, classes, and even vectors. For example:
#include <vector>
#include <string>
int main()
{
  vector<int>    list1;
  vector<string> list2;
  vector<Foo>    list3;
  vector<Foo *>  list4;
}
In order to declare a vector, you must include <vector> as a libary.

list1 is a vector of int's. list2 is a vector of string's. list3 is a vector of Foo's. list4 is a vector of Foo pointers. As you can see, you can pretty much store anything.

I really shouldn't say anything because it is useful to have a few operations in the class, if they are to be used in vectors:

Fortunately, nearly every class has these methods defined.

When you declare a vector, initially the size of the vector is 0. That means, it can store no elements at all. There are two ways to store elements. First, you can specify the size of the vector, when declaring it.

#include <vector>
#include <string>
int main()
{
  vector<Foo>    list1( 5 );
}
In this case, a vector with 5 elements is created, and it is created using the default constructor. Be careful not to use the [], and use the () instead (after all, this is an object, not a true array).

Using push_back

Another way to increase the size of a vector is to push an element at the end, using the push_back method.

For example,

#include <vector>
#include <string>
int main()
{
  string s1 = "cat", s2 = "dog";
  vector<string>   list1;

  list1.push_back( s1 );
  // prints 1, since size is 1
  cout << list1.size() << endl;

  list1.push_back( s2 );
  // prints 2, since size is 2
  cout << list1.size() << endl;
}
In general, we will use this method of increasing the size of the vector.

Using resize

You can also call "resize" to increase the size of a vector. For example, you can say:
#include <vector>
#include <string>
int main()
{
  string s1 = "cat", s2 = "dog";
  vector<string>   list1;

  list1.resize( 5 );
  list1.resize( 10, s2 );
}
resize takes one or two arguments. If it only has one argument, then it will increase the size of the vector (assuming the size passed in is larger than the current size of the vector). Each new element in the vector will be constructed using the default constructor.

If you use two arguments, then the second argument will be an instance of the class, and each new element constructed will be a copy of this instance.

If the argument, size, is smaller than the actual size of the vector, then the vector simply decreases in size.

Making a vector smaller

There are two ways to decrease the size of a vector. As shown in the last section, one such way is to decrease the size is to use the resize() method. A second way is to call the "pop_back" method. This method reduces the size of a vector by 1 (and does not return the element, in the process).
  vector<string>   list1( 5 );

  list1.pop_back();
  // prints 4
  cout << list1.size() << endl;
}

Random access

Vectors are basically arrays, and arrays allow for random access using []. You can use [] to access elements of a vector as well.
  vector<string>   list1( 5 );

  int size = static_cast<int>( list1.size() );
  for ( int i = 0; i < size; i++ )
    {
      cout << list1[ i ] << endl;
    }
}
The only problem with vectors is that the size() method returns back an unsigned int, instead of an int. This makes some sense, since vectors can't have negative signs.

However, this makes iterating through a vector something of a pain. Most programmers prefer to use variables of type int to loop through an array. In order to do this successfully, you need to use a static cast, to convert the unsigned int size, to a signed int size. See the example above.

If you don't do this, more than likely, the compiler will produce a lot of error messages.

Sorting

If you use a vector, you can actually sort very easily. STL has a "sort algorithm". To use this "algorithm", you must include <algorithm> as a library. Furthermore, the objects contained in the vector MUST define the following methods: where "Obj" is replaced by the object type used in the vector. Again, be very careful to use exactly the same prototypes listed above. With these two operations, the vector can sort. Without them, it can't, and will produce lots of error messages, when compiling.

For example,

#include <vector>
#include <algorithm>
int main()
{
  vector<Foo>   list( 5 );

  sort( list.begin(), list.end() );
}
Thus, to sort a vector of Foo objects, the class Foo will need to define operator< and operator==.

The vector will be sorted in ascending order. To sort in descending order, you sort in ascending order, then call

  reverse( list.begin(), list.end() );
which reverses the elemenst of a list.

There is one more unusual aspect of sorting. You can sort part of a vector if you want. To do so, you have to indicate which index to begin sorting and which index to end. We won't look at this kind of sorting now. since it's typical to sort the entire vector.

To sort the entire vector, you must pass the name of the vector with .begin() at the end as the first argument, then the name of the vector with .end().

thus,

  sort( list.begin(), list.end() );
will sort the entire vector. You will understand more of what begin() and end() later on when STL is covered more in depth. For the time being, the goal is to have you learn enough about vectors to use them, and to avoid making too many errors when compiling.