Vector
Overview
An STL vector represents a dynamically expandable array of one dimension. Implementations of vector emphasize high-speed random access. Like an array, elements of a vector are stored contiguously. This makes random access a simple (and fast) pointer arithmetic operation. Vectors can be used anywhere arrays are needed. Here are some more qualities that make the vector container attractive:
To declare an instance of the STL vector class template, you must #include <vector>. Like all of the C++ Standard Library, vector is declared within the std namespace. The following example demonstrates how to declare a vector of integers.
#include <vector>
using namespace std;
vector<int> aVector;
Accessing the Elements of a Vector
You can access the elements of a vector in much the same way as you access elements of an array, by using the bracket ([]) operator. For example,
aVector[3] = 100;
int b = aVector[5];
Use the bracket operator when you want fast, unvalidated access to the elements of a vector. However, if you want vector to validate the index you pass to it, use the at() method instead. at() accepts an integer index and returns a reference to the element at the requested index. If the index is invalid, then at() throws an exception of class out_of_range, as shown in the following example:
/*
ComputeSum()
Description: For a vector of some numeric type T, returns the sum of the elements in the input vector. Throws an exception of class out_of_range if an attempt to access an invalid element index is made.
Arguments: values [input] - a vector of values for which the sum is to be computed
*/
template <class T>
T ComputeSum(const vector<T> &values)
{
T sum=T(); // initialize sum
try
{
for (int i=0; i<values.size(); ++i)
sum+=values.at(i);
}
catch (out_of_range e)
{
cerr << "Error in ComputeSum: Invalid vector index.\n";
throw; // throw the exception out another level
}
return sum;
}
Of course, this example is trivial because it can be shown that this function will only ever access valid vector elements, but it still demonstrates how to use the at() method to ensure that vector indexes are correct.
Expanding a Vector
An element can be added to a vector by calling the push_back() method. push_back() appends a value to the end of the sequence contained by the vector, and causes the size of the vector to increase by 1. For example,
aVector.push_back(82);
Since a vector stores elements in a single contiguous memory block, it would not be efficient to permit values to be inserted at anywhere but the end of a vector. For this reason, vector only allows elements to be appended to the end.
Storage Management Considerations
To increase efficiency, vector implementations maintain a buffer of unused memory so that the entire vector need not be reallocated and moved every time an element is added. However, if this buffer is used up and another element is added to the end of the vector, a new, larger block of memory is allocated and all of the elements must be copied to this newly allocated block of memory.
Obviously, the number of times that the vector needs to be reallocated should be kept to a minimum. If you know approximately how many elements will be stored in the vector, you can pre-allocate storage for a specific number of elements by calling the reserve() method.
A vector can be resized (truncated or lengthened) by calling the resize() method. The new elements, if any, are instantiated and the default constructor for the element type is invoked. Note that a vector will never shrink the amount of memory allocated to itself, but you can reduce the number of elements it contains.
Iterators to vector elements must be considered invalid after a call to push_back(), reserve(), or resize(). This is because any of these operations can cause the vector to be reallocated and moved, thus invalidating any previous references to vector elements.
The following example program demonstrates how to pass a vector to a function that expects an array.
#include <vector>
using namespace std;
double Mean(const double ar[], int extent)
{
double sum=0.0;
for (int i=0; i<extent; ++i)
sum+=ar[i];
return sum/extent;
}
main()
{
vector<double> x;
x.push_back(5.1);
x.push_back(10.7);
// Pass the address of the first element, plus the "extent" of the vector.
// Extent refers to the number of elements an array contains.
cout << Mean(&x[0], x.size());
}
This works only because vector elements are guaranteed to be stored contiguously, like arrays.
Recall that arrays in C++ are implicitly passed by reference. This must be the case since arrays do not track their own size. If the compiler attempted to pass an array by value, I could provide example cases where the compiler could not possibly determine the size of the array. These "by reference" semantics do have one positive side effect, however. It makes passing an array to a function very efficient in terms of both space and time. When using a vector, you must remember that passing it by value will generate a temporary copy of the vector. Therefore, pass a vector by constant reference when you want to read access to its elements, and by reference when you want read/write access to its elements. I point this out because a vector behaves so similarly to an array, it is easy to make this mistake.
For example,
double Mean(vector<double> vec);
should be replaced with:
double Mean(vector<double> &vec);
OR double Mean(const vector<double> &vec);
Methods
|
Operation |
Description |
Time Complexity |
|
operator= |
Allows assignment of one vector into another. Both vectors must contain elements of the same data type. Ex: v1 = v2; |
O(n) |
|
swap |
Swaps elements with another vector. Since this only involves a pointer exchange, this operation can be performed in constant time. Ex: v1.swap(v2); |
O(1) |
|
begin |
Returns an iterator to the first value in the sequence contained by the vector. Ex: itr = v1.begin(); |
O(1) |
|
end |
Returns an iterator to an object that is one element beyond the last valid value in the sequence. This iterator should not be dereferenced, as it is intended to indicate the end of a sequence. Ex: if (itr == v1.end())
; // end of sequence found |
O(1) |
|
operator[] |
Returns a reference to the vector element at a given index. If the index is not valid, then the behavior is undefined. Ex: v1[3] = 100; |
O(1) |
|
at |
Returns a reference to the vector element at a given index. If the index is not valid, then an exception of class out_of_range is thrown. |
O(1) |
|
erase |
There are two forms of this method. The first accepts a single iterator and erases the element implied by that iterator. The size of the sequence is decreased by 1. The second accepts two iterators and erases the subsequence implied by those iterators. The size of the sequence is decreased by the length of the subsequence that was erased. Ex: v1.erase(v1.begin(), v1.end()); // erase the entire vector |
O(n) |
|
push_back |
Appends an element to the end of the sequence contained by the vector, thus increasing the size of the vector by 1. If reallocation must occur, then this is an O(n) operation. Ex: v1.push_back(72); |
O(1) average O(n) worst |
|
pop_back |
Removes the last element in the sequence contained by the vector. |
O(1) |
|
reserve |
After a call to this method, the internal contiguous memory block used to contain vector elements is guaranteed to be large enough to contain the number of elements specified. Use this method when you have a good estimate of the number of elements that the vector will contain, so that reallocations will be kept to a minimum. This does not affect the size of the vector, it only increases the capacity. Ex: v1.reserve(500); // reserves space for 500 elements |
O(1) best O(n) worst |
|
resize |
Expands or truncates the number of elements contained by the vector. Ex: v1.resize(10); // the vector now contains 10 elements |
O(1) best. O(n) worst. |
|
size |
Returns the number of elements contained by the vector. |
O(1) |
|
empty |
Returns true if the vector is empty. |
O(1) |
For the examples listed above, <vector> is assumed to be included, and the following declarations are assumed.
std::vector<int> v1;
std::vector<int> v2;
std::vector<int>::iterator itr;
Additional notes on interpreting this table can be found in the document on the list container.