Dynamic Array std::vector in C++

std::vector stands as a cornerstone in the C++ Standard Template Library (STL), providing a dynamic array implementation that combines flexibility, efficiency, and ease of use.

Introduction to std::vector

std::vector is a dynamic vector container in C++, offering a resizable array-like structure that grows and shrinks dynamically. It is part of the STL providing a high-level interface for managing sequences of elements.

Declaration and Initialization

Creating a std::vector is straightforward:

#include <vector>

std::vector<int> myVector;  // Declare an empty vector of integers

You can also initialize a vector with a specified size and default values:

std::vector<int> initializedVector(5, 0);  // Create a vector of size 5, initialized with zeros

Dynamic Resizing

One of the standout features of std::vector is its ability to resize dynamically. Elements can be added or removed, and the vector manages the underlying memory for you:

myVector.push_back(42);  // Add an element to the end
myVector.pop_back();     // Remove the last element

Accessing Elements

Accessing elements in a std::vector is akin to traditional arrays:

int value = myVector[2];      // Access the third element
int lastValue = myVector.back();  // Access the last element

Iterating through Elements

std::vector supports iterators for seamless traversal of its elements:

for (auto it = myVector.begin(); it != myVector.end(); ++it) {
    // Access and manipulate each element using '(*it)'
}

The range-based for loop provides more concise syntax:

for (const auto &element : myVector) {
    // Access and manipulate each element directly
}

Dynamic Memory Allocation

Internally, std::vector dynamically allocates memory to accomodate its elements. As elements are added, the vector automatically reallocates memory when necessary.

std::vector<int> dynamicVector;
dynamicVector.reserve(100);  // Preallocate memory for 100 elements

The reserve function allows you to allocate memory in advance, minimizing reallocation during subsequent operations.

Automatic Resizing

Adding elements to a std::vector triggers automatic resizing when the current capacity is exceeded. The vector allocates a larger chunk of memory, copies existing elements, and updates its size and capacity.

for (int i = 0; i < 1000; ++i) {
    dynamicVector.push_back(i);
}

This automatic resizing ensures optimal memory usage while abstracting the process from the programmer.

Member Functions

push_back and pop_back:

  • push_back(const T& value): Adds an element to the end of the vector. If the vector is full, it triggers automatic resizing.
std::vector<int> myVector;
myVector.push_back(42);  // Adds 42 to the end of the vector
  • pop_back(): Removes the last element from the vector. This operation doesn't deallocate memory, but it decreases the vector's size
myVector.pop_back();  // Removes the last element

size and capacity:

  • size(): Returns the number of elements in the vector.
std::cout << "Size of the vector: " << myVector.size() << std::endl;
  • capacity(): Returns the current capacity of the vector, which represents the maximum number of elements it can hold before resizing.
std::cout << "Capacity of the vector: " << myVector.capacity() << std::endl;

reserve and shrink_to_fit:

  • reserve(size_type n): Increases the capacity of the vector to at least n elements. This can help prevent frequent reallocations.
myVector.reserve(100);  // Reserve memory for at least 100 elements
  • shrink_to_fit(): Reduces the vector's capacity to fit its size. This is useful if you want to release excess memory.
myVector.shrink_to_fit();  // Reduce capacity to fit the size

clear and empty:

  • clear(): Removes all elements from the vector, leaving it with a size of 0.
myVector.clear();  // Removes all elements
  • empty(): Returns true if the vector is empty (size is 0), false otherwise.
if (myVector.empty()) {
    std::cout << "Vector is empty." << std::endl;
}

resize and assign:

  • resize(size_type n, const T& value): Changes the size of the vector to n, and if it increases in size, fills the new elements with the specified value.
myVector.resize(10, 42);  // Resize to 10 elements, filling new elements with 42
  • assign: Assigns new values to the vector replacing its current content.
myVector.assign({1, 2, 3, 4, 5});  // Assign a new set of values

Iterators

  • begin() and end(): Return iterators pointing to the first and one past the last element of the vector, respectively.
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
    // Access and manipulate each element using '(*it)'
}
  • rbegin() and rend(): Returns reverse iterators, allowing you to iterate in reverse order.
for (auto rit = myVector.rbegin(); rit != myVector.rend(); ++rit) {
    // Access and manipulate each element in reverse order
}

Element Access

  • operator[] and at: Provide access to the elements in this vector. The main difference is that at performs bounds checking.
int value = myVector[2];      // Access the third element
int safeValue = myVector.at(2);  // Access with bounds checking