Dynamic arrays supply the programmer with the flexibility of storing data without needing to know the exact volume thereof at the time of programming the application, the way static arrays do. Naturally, this is a frequently needed requirement and the STL supplies a ready-to-use solution in the form of the std::vector class.
The Characteristics of std::vector
- Addition of elements to the end of the array in constant time; that is, the time needed is not dependent on the size of the array. Ditto for removal of an element at the end.
- The time required for the insertion or removal of elements at the middle is directly proportional to the number of elements behind the element being removed.
- The number of elements held is dynamic and the vector class manages the memory usage.
#include <vector>
int main ()
{
std::vector <int> vecDynamicIntegerArray;
// Instantiate a vector with 10 elements (it can grow larger)
std::vector <int> vecArrayWithTenElements (10);
// Instantiate a vector with 10 elements, each initialized to 90
std::vector <int> vecArrayWithTenInitializedElements (10, 90);
// Instantiate one vector and initialize it to the contents of another
std::vector <int> vecArrayCopy (vecArrayWithTenInitializedElements);
// Instantiate a vector to 5 elements taken from another
std::vector<int> vecSomeElementsCopied(vecArrayWithTenElements.begin()
, vecArrayWithTenElements.begin () + 5);
return 0;
}
Inserting Elements in a vector
#include <iostream>
#include <vector>
int main ()
{
std::vector <int> vecDynamicIntegerArray;
// Insert sample integers into the vector:
vecDynamicIntegerArray.push_back (50);
vecDynamicIntegerArray.push_back (1);
vecDynamicIntegerArray.push_back (987);
vecDynamicIntegerArray.push_back (1001);
std::cout << "The vector contains ";
std::cout << vecDynamicIntegerArray.size () << " Elements";
return 0;
}
push_back, as seen in lines 9–12 is the vector class’s public member method that inserts objects at the end of the dynamic array. Note the usage of function size (), whichreturns the number of elements held in the vector.
A different way of inserting the values than the way shown earlier would be by specifying the number of elements to be kept in the vector and then copying values into individuallocations as in an array.
#include <vector>
#include <iostream>
int main ()
{
std::vector <int> vecDynamicIntegerArray (4);
// Copy integer values into individual element locations
vecDynamicIntegerArray [0] = 50;
vecDynamicIntegerArray [1] = 1;
vecDynamicIntegerArray [2] = 987;
vecDynamicIntegerArray [3] = 1001;
std::cout << "The vector contains ";
std::cout << vecDynamicIntegerArray.size () << " Elements";
return 0;
}
Line 6 constructs the vector object with four integers.
std::vector features an insert function where you can specify the position at which elements can be inserted into the sequence.
#include <vector>
#include <iostream>
int main ()
{
using namespace std;
// Instantiate a vector with 4 elements, each initialized to 90
vector <int> vecIntegers (4, 90);
cout << "The initial contents of the vector are: ";
vector <int>::iterator iElement;
for ( iElement = vecIntegers.begin ()
; iElement != vecIntegers.end ()
; ++ iElement )
{
cout << *iElement << ' ';
}
cout << endl;
// Insert 25 at the beginning
vecIntegers.insert (vecIntegers.begin (), 25);
cout << "The vector after inserting an element at the beginning: ";
for ( iElement = vecIntegers.begin ()
; iElement != vecIntegers.end ()
; ++ iElement )
{
cout << *iElement << ' ';
}
cout << endl;
// Insert 2 numbers of value 45 at the end
vecIntegers.insert (vecIntegers.end (), 2, 45);
cout << "The vector after inserting two elements at the end: ";
for ( iElement = vecIntegers.begin ()
; iElement != vecIntegers.end ()
; ++ iElement )
{
cout << *iElement << ' ';
}
cout << endl;
// Another vector containing 2 elements of value 30
vector <int> vecAnother (2, 30);
// Insert two elements from another container in position [1]
vecIntegers.insert (vecIntegers.begin () + 1,
vecAnother.begin (), vecAnother.end ());
cout << "The vector after inserting contents from another ";
cout << "in the middle:" << endl;
for ( iElement = vecIntegers.begin ()
; iElement != vecIntegers.end ()
; ++ iElement )
{
cout << *iElement << ' ';
}
return 0;
}
Output:
The initial contents of the vector are: 90 90 90 90
The vector after inserting an element at the beginning: 25 90 90 90 90
The vector after inserting two elements at the end: 25 90 90 90 90 45 45
The vector after inserting contents from another container in the middle:
25 30 30 90 90 90 90 45 45
Accessing Elements in a vector
#include <iostream>
#include <vector>
int main ()
{
using namespace std;
vector <int> vecDynamicIntegerArray;
// Insert sample integers into the vector:
vecDynamicIntegerArray.push_back (50);
vecDynamicIntegerArray.push_back (1);
vecDynamicIntegerArray.push_back (987);
vecDynamicIntegerArray.push_back (1001);
unsigned int nElementIndex = 0;
while (nElementIndex < vecDynamicIntegerArray.size ())
{
cout << "Element at position " << nElementIndex;
cout << " is: " << vecDynamicIntegerArray [nElementIndex] << endl;
++ nElementIndex;
}
return 0;
}
Output:
Element at position 0 is: 50
Element at position 1 is: 1
Element at position 2 is: 987
Element at position 3 is: 1001
Accessing elements in a vector using [] is fraught with the same dangers as accessing elements in an array; that is, you should not cross the bounds of the container. If you use the subscript operator[] to access elements in a vector at a position that is beyond its bounds, the result of the operation will be undefined (anything could happen, possibly an access violation).
A safer alternative is to use the at() member function:
// gets element at position 2
cout << vecDynamicIntegerArray.at (2);
// the vector::at() version of the code above in Listing
➥18.5, line 20:
cout << vecDynamicIntegerArray.at (nElementIndex);
at() performs a runtime check against the size of the container and throws an exception if you cross the boundaries.Note that the subscript operator[] is safe to use when done in a manner that ensures bound integrity, as in the sample above.
Elements in a vector can also be accessed using pointer-like semantics by the use of iterators.
#include <iostream>
#include <vector>
int main ()
{
using namespace std;
vector <int> vecDynamicIntegerArray;
// Insert sample integers into the vector:
vecDynamicIntegerArray.push_back (50);
vecDynamicIntegerArray.push_back (1);
vecDynamicIntegerArray.push_back (987);
vecDynamicIntegerArray.push_back (1001);
// Access objects in a vector using iterators:
vector<int>::iterator iElementLocator = vecDynamicIntegerArray.begin();
while (iElementLocator != vecDynamicIntegerArray.end ())
{
size_t nElementIndex = distance (vecDynamicIntegerArray.begin (),
iElementLocator);
cout << "Element at position ";
cout << nElementIndex << " is: " << *iElementLocator << endl;
// move to the next element
++ iElementLocator;
}
return 0;
}
Output:
Element at position 0 is: 50
Element at position 1 is: 1
Element at position 2 is: 987
Element at position 3 is: 1001
Removing Elements from a vector
#include <iostream>
#include <vector>
int main ()
{
using namespace std;
vector <int> vecDynamicIntegerArray;
// Insert sample integers into the vector:
vecDynamicIntegerArray.push_back (50);
vecDynamicIntegerArray.push_back (1);
vecDynamicIntegerArray.push_back (987);
vecDynamicIntegerArray.push_back (1001);
cout << "The vector contains ";
cout << vecDynamicIntegerArray.size ();
cout << " elements before calling pop_back" << endl;
// Erase one element at the end
vecDynamicIntegerArray.pop_back ();
cout << "The vector contains ";
cout << vecDynamicIntegerArray.size ();
cout << " elements after calling pop_back" << endl;
cout << "Enumerating items in the vector... " << endl;
unsigned int nElementIndex = 0;
while (nElementIndex < vecDynamicIntegerArray.size ())
{
cout << "Element at position " << nElementIndex << " is: ";
cout << vecDynamicIntegerArray [nElementIndex] << endl;
// move to the next element
++ nElementIndex;
}
return 0;
}
Output:
The vector contains 4 elements before calling pop_back
The vector contains 3 elements after calling pop_back
Enumerating items in the vector...
Element at position 0 is: 50
Element at position 1 is: 1
Element at position 2 is: 987
The output indicates that the pop_back function used at line 20 has reduced the elements in the vector by erasing the last element inserted into it. Line 24 calls size() again to demonstrate that the number of elements in the vector has reduced by one, as indicated in the output.
Understanding size() and capacity()
The size of a vector is the actual number of elements stored in a vector. The capacity of a vector is the total number of elements that can potentially be stored in the vector before it reallocates memory to accommodate more elements. Therefore, a vector’s size is less than or equal to its capacity.
#include <iostream>
#include <vector>
int main ()
{
using namespace std;
// Instantiate a vector object that holds 5 integers of default value
vector <int> vecDynamicIntegerArray (5);
cout << "Vector of integers was instantiated with " << endl;
cout << "Size: " << vecDynamicIntegerArray.size ();
cout << ", Capacity: " << vecDynamicIntegerArray.capacity () << endl;
// Inserting a 6th element in to the vector
vecDynamicIntegerArray.push_back (666);
cout << "After inserting an additional element... " << endl;
cout << "Size: " << vecDynamicIntegerArray.size ();
cout << ", Capacity: " << vecDynamicIntegerArray.capacity () << endl;
// Inserting another element
vecDynamicIntegerArray.push_back (777);
cout << "After inserting yet another element... " << endl;
cout << "Size: " << vecDynamicIntegerArray.size ();
cout << ", Capacity: " << vecDynamicIntegerArray.capacity () << endl;
return 0;
}
Output:
Vector of integers was instantiated with
Size: 5, Capacity: 5
After inserting an additional element...
Size: 6, Capacity: 7
After inserting yet another element...
Size: 7, Capacity: 7
The STL deque Class
deque (pronunciation rhymes with deck) is an STL dynamic array class quite similar in properties to that of the vector except that it allows for the insertion and removal of elements at the front and back of the array.
As the code below shows, it bears a remarkable similarity to using std::vector.
#include <deque>
#include <iostream>
#include <algorithm>
int main ()
{
using namespace std;
// Define a deque of integers
deque <int> dqIntegers;
// Insert integers at the bottom of the array
dqIntegers.push_back (3);
dqIntegers.push_back (4);
dqIntegers.push_back (5);
// Insert integers at the top of the array
dqIntegers.push_front (2);
dqIntegers.push_front (1);
dqIntegers.push_front (0);
cout << "The contents of the deque after inserting elements ";
cout << "at the top and bottom are:" << endl;
// Display contents on the screen
for ( size_t nCount = 0
; nCount < dqIntegers.size ()
; ++ nCount )
{
cout << "Element [" << nCount << "] = ";
cout << dqIntegers [nCount] << endl;
}
cout << endl;
// Erase an element at the top
dqIntegers.pop_front ();
// Erase an element at the bottom
dqIntegers.pop_back ();
cout << "The contents of the deque after erasing an element ";
cout << "from the top and bottom are:" << endl;
// Display contents again: this time using iterators
deque <int>::iterator iElementLocator;
for ( iElementLocator = dqIntegers.begin ()
; iElementLocator != dqIntegers.end ()
; ++ iElementLocator )
{
size_t nOffset = distance (dqIntegers.begin (), iElementLocator);
cout<<"Element [" << nOffset << "] = " << *iElementLocator<<endl;
}
return 0;
}
Output:
The contents of the deque after inserting elements at the top and bottom are:
Element [0] = 0
Element [1] = 1
Element [2] = 2
Element [3] = 3
Element [4] = 4
Element [5] = 5
The contents of the deque after erasing an element from the top and bottom are:
Element [0] = 1
Element [1] = 2
Element [2] = 3
Element [3] = 4