Understanding C++ Vectors: A Better Alternative to Static Arrays

Understanding C++ Vectors: A Better Alternative to Static Arrays

Imagine you’re organizing a tournament where you need to track player scores, but you don’t know how many players will participate. One option is to use a static array, which requires specifying a fixed size. However, this approach is risky—if the tournament gains unexpected popularity, your array may be too small, leading to issues.

A better solution is to use a dynamic array such as a vector, which can grow and shrink as needed at runtime.

What is a Vector?

A vector is a dynamic container provided by the C++ Standard Library. It offers:

  • Dynamic resizing: Can grow and shrink in size during execution

  • Array-like syntax and semantics: Easy to use if you're familiar with arrays

  • Bounds checking: Helps prevent accessing out-of-range elements

  • Powerful built-in functions: Includes functions like sort(), reverse(), find(), and more

  • Efficiency: Vectors provide optimized performance

How to Declare Vectors

Declaring a vector is slightly different from declaring an array since vectors are objects. First, you need to include the vector library:

Declaring an Empty Vector

#include <vector>
using namespace std;

int main()
{
    vector<int> player_scores;  // An empty vector of integers
}

Declaring a Vector with a Specific Size

#include <vector>
using namespace std;

int main()
{
    vector<int> player_scores(10);  // A vector of size 10, all initialized to 0
}

Declaring and Initializing a Vector with Values

#include <vector>
using namespace std;

int main()
{
    vector<int> player_scores {10, 5, 8, 12};  // A vector initialized with specific values
}

Accessing Vector Elements

There are two primary ways to access elements in a vector:

Using Subscript Notation []

Vectors allow you to access elements using an index inside square brackets, just like arrays.

vector<int> scores {100, 95, 99, 87, 88};
cout << scores[0];  // Outputs: 100

However, this method does not provide bounds checking, so an out-of-range index can cause unexpected behavior.

Using the at() Method

A safer approach is to use the at() method, which includes bounds checking.

vector<int> scores {100, 95, 99, 87, 88};
cout << scores.at(0);  // Outputs: 100

If an invalid index is used, the program will throw an out-of-range exception, which helps prevent crashes.

out-of-range exception

Modifying Vector Elements

Vectors allow element modification just like arrays:

vector<int> scores {100, 95, 99, 87, 88};
scores[0] = 90;  // Modify the first element
cout << scores.at(0);  // Outputs: 90

Adding Elements to a Vector

The push_back() method allows you to dynamically add new elements to the end of a vector.

vector<int> scores {100, 95, 99};
scores.push_back(80);  // Adds 80 to the vector
scores.push_back(90);  // Adds 90 to the vector

After these operations, the vector will contain {100, 95, 99, 80, 90}.

Checking Vector Size

To determine the number of elements in a vector, use the size() method.

vector<int> scores {100, 95, 99, 80, 90};
cout << "Number of scores: " << scores.size();  // Outputs: 5

Handling Out-of-Bounds Errors

If you try to access an element beyond the vector’s size using at(), an exception will be thrown.

vector<int> scores {100, 95};
cout << scores.at(5);  // Throws an out-of-range exception

Exception handling can be used to prevent program crashes:

try
{
    vector<int> scores {100, 95};
    cout << scores.at(5);
}
catch (out_of_range &e)
{
    cout << "Error: " << e.what(); // Error: vector::_M_range_check: __n (which is 5) >= this->size() (which is 2)
}

Using Two-Dimensional Vectors

Vectors can also store other vectors, effectively creating a 2D vector.

vector<vector<int>> movie_ratings {
    {1, 2, 3, 4},
    {2, 3, 4, 5},
    {3, 4, 5, 6}
};
cout << movie_ratings[0][1];  // Outputs: 2

Key Features of Vectors

Vectors have several important characteristics that make them a better choice over static arrays:

  • Dynamic Size: Can change at runtime, unlike static arrays

  • Elements of the Same Type: Like arrays, all elements must be of the same data type

  • Stored Contiguously: Data is stored in a continuous block of memory

  • Index-Based Access: Individual elements are accessed using an index.

  • Zero-Based Indexing: The first element is at index 0, and the last is at size - 1

  • Bounds Checking:

    • Using [] does not check for out-of-bounds access

    • Using functions like at() ensures safe access

  • Automatic Initialization: Elements are automatically initialized to zero if no values are provided

  • Iteration Support: Loops (e.g., for loops) are commonly used to process vector elements


Discover hands-on programming tutorials and resources! Visit my website: Fullstack Dev