C++ has a variety of different containers types that can be used to store data. The most common amongst them is the array, which is the standard approach that is commonly taught and used across most programming languages. C++ Vector groups however, are really useful and can replace/outperform arrays in several areas.

We’ll discuss the benefits of Vector groups, as well as how to use them and the various options they support, in this C++ tutorial. There’s also a short segment at the end discussing a few peculiarities of the vector group, and how to get around them.


Why we use Vectors in C++?

The biggest benefit that Vectors provide, is the automatic memory management that makes your coding process much simpler. You don’t have to worry about allocating space, or re-sizing the Vector once the limit of values has been reached. Vectors automate the whole process by automatically doubling it’s capacity every time the limit is reached.

Furthermore, Vectors are actually a template type, which helps you to write “generic” code that can be used for data of any type. They also come with alot of functions that are really useful, such as size() which returns the number of elements in the C++ vector group.


Creating a Vector

There are several different ways of declaring and initializing Vectors in C++. Let’s take a look at some of them.

The first thing you need to do is import the <vector> library, and for simplicity, use the namespace std. (Else you will have to do std::vector)

#include <vector>
using namespace std;

Next let’s create a vector group.

vector<string> vec;

This creates an empty Vector that can hold strings. Although it’s empty right now, we can use the push_back() operator later to move in new elements.

Remember, since Vectors are templates, they can be created to store data of any type. However, they can only store one type at a time, with which they were declared.

vector<string> grocery = { "Apples", "Bananas", "Milk" };

The above format can be used to initialize a vector with some values in it.

vector<int> vec(10);
vector<string> vec(10);

The above method creates 2 vectors groups of type int and string respectively with 10 empty elements. The empty int slots are initlizaed to 0, and the strings are empty by default. (Notice that these are round brackets, not curly)

vector<int> vec(10, 1);

The above format will create 10 elements of type int, with the value 1. Useful for initializing with a custom value.


Vector Operations

In this section we will try to cover as many as the available Vector operations as possible.

Adding and Removing Elements to a Vector

A common misconception is that you can add elements to a vector group using the [] operator. This is not the case, and you can only add elements at the declaration time or using push_back(), which pushes a new element to the back of the vector group.

vector<int> vec;

vec.push_back(5);
vec.push_back(3);
vec.push_back(7);

for (int i = 0; i < vec.size(); i++) {
    cout << vec[i] << endl;
}

Output:

5
3
7

Similarly to the push_back() function, pop() removes the last element from the vector group.

vec.push_back(5);
vec.push_back(3);
vec.push_back(7);

vec.pop_back();
vec.pop_back();

for (int i = 0; i < vec.size(); i++) {
    cout << vec[i] << endl;
}

Output:

5

Size Related Operations

size() returns an integer “n”, which is the number of elements in the vector.

capacity() returns the current number of items that the vector can hold with the space currently allocated to it.

max_size() returns the maximum number of values that a vector can hold (by continuously expanding). Not to be confused with capacity.

reserve(n) reserves space (capacity) for an “n” number of elements in the vector.

shrink_to_fit() shrinks the vector group to fit it’s capacity. Any extra elements are removed.

resize(n) resizes the vector group’s capacity to a number “n”.

empty() returns True is the vector is empty, else returns false.

Example Code

The below code demonstrates how the Vector expands as elements are added into it, as well as the use of several other Vector related functions.

int main() {
    vector<int> vec;

    vec.push_back(2);
    vec.push_back(1);

    cout << "Size: " << vec.size() << endl;
    cout << "Capacity: " << vec.capacity() << endl;

    vec.push_back(6);

    cout << "Size: " << vec.size() << endl;
    cout << "Capacity: " << vec.capacity() << endl;

    vec.pop_back();
    vec.pop_back();

    cout << "Size: " << vec.size() << endl;
    cout << "Capacity: " << vec.capacity() << endl;

    vec.shrink_to_fit();

    cout << "Size: " << vec.size() << endl;
    cout << "Capacity: " << vec.capacity() << endl;
}


Iterator Operations

begin() returns an iterator to the vector group which points to the first element.

rbegin() returns an iterator to the vector group, that points to the last element.

end() returns an iterator to the vector group, that points to the theoretical element after the last element.

rend() returns an iterator to the vector group, that points to the theoretical element before the first element.


Nested Vectors

The syntax may seem a little confusing, but you can actually nest vectors into each other just like how you do with Arrays.

vector<vector<Object> > v1;

You can treat them how would a regular 2D array, iterating through them with two for loops, or accessing individual elements with the double [][] operator.


Removing an Element from a Vector

You need to be careful when removing elements from C++ vector groups. Using “erase” to remove an element from the vector, while iterating over it with a for loop will actually cause problems. The reason being that the size of the vector changes after the erase operation, hence the next iterations could be invalid as they will be using the old size as reference.

Luckily, there is an easy way to get around this. Using “iterators” and the below while loop format, you can safely remove elements from any vector group.

The below code is from a game, where objects of a custom Class Particle are in a vector group. These are meant to be “smoke particles”, hence the Alpha values are gradually reduced. Once they are 0, we want them to be removed from the Vector group.

        vector<Particle>::iterator i;
        for (i = particles.begin(); i != particles.end();) {
            if (i->getAlpha() <= 0) {
                i = particles.erase(i);
            }
            else
            {
                ++i;
            }
        }

The first thing we did was create an iterator of type Vector<Particle>. Next we begin the for loop using begin() and end(). The most important thing is the if statement here. There are two possible scenarios. Either the element that the iterator is current pointing to, meets the “conditions” for removal (Alpha less than or equal to 0 in our case) or we increment it, to point to the next element in the array.

In this style, we can safely remove elements from a vector.


Dealing with Vector Expansion

When vectors expand, all the objects in it are first copied over to the new (and larger) vector group, and then the old objects are destroyed. This can actually cause problems in certain scenarios, such as the destructor for the destroyed objects being called, and memory locations changing after the move.

If you have a pointer to any element in the vector group, it will become invalid after the vector expansion as the memory address changes. A good way to get around this is to smart pointers, which still retain the correct reference.

A simpler and short term solution would be to set a large enough initial size for the vector group, before passing in values.


This marks the end of the C++ Vector Groups Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments