Quick Answer

A C++ vector (std::vector) is a dynamic array from the Standard Template Library that grows and shrinks while your program runs, so you never manage the memory by hand. You add items with push_back, read them with v[i] or the bounds-checked v.at(i), and loop over them with a range-based for. For almost all everyday C++ code, prefer a vector over a raw array.

What is a C++ vector?

A C++ vector (std::vector) is a resizable array from the Standard Template Library (STL). A plain array in C++ has a fixed size that you decide at compile time — once you write int marks[30]; you are stuck with 30 slots. A vector removes that limit: it grows and shrinks while your program runs, and it looks after the memory for you.

Think of it as a smarter array. You still get fast index access (v[i]), your data still sits in one continuous block of memory, but you also get handy methods like push_back(), size(), and automatic clean-up. For beginners this means fewer crashes and no manual new/delete.

To use it, include the header and, optionally, pull in the std namespace:

#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> marks;   // an empty vector of ints
    marks.push_back(90);
    cout << marks.size() << "\n";  // 1
}

How to declare a vector

The type inside the angle brackets is the type of element the vector holds. vector<int> stores ints, vector<string> stores strings, and so on. Here are the common ways to create one:

vector<int> a;              // empty, size 0
vector<int> b(5);           // 5 ints, each set to 0
vector<int> c(5, 42);       // 5 ints, each set to 42
vector<int> d = {1, 2, 3};  // list of values
vector<int> e(d);           // a copy of d

The pattern vector<int> c(5, 42) is worth remembering: the first number is the count, the second is the value to fill with. Mixing these up is a common early mistake, so read it as "five copies of forty-two".

Adding elements with push_back

push_back() adds one element to the end of the vector and makes it one longer. This is the everyday way to fill a vector when you do not know the size ahead of time — for example, reading numbers until the input ends.

vector<int> marks;
marks.push_back(90);
marks.push_back(85);
marks.push_back(78);
// marks is now {90, 85, 78}, size 3

A few related methods you will use often:

  • pop_back() — removes the last element.
  • back() and front() — read the last / first element.
  • emplace_back() — like push_back but builds the element in place; handy for objects.
  • clear() — removes everything, leaving size 0.

Accessing elements: [] vs at()

There are two ways to read or write an element by position. Both use zero-based indexing, so the first element is index 0.

vector<int> v = {10, 20, 30};
cout << v[1] << "\n";     // 20  — square brackets
cout << v.at(1) << "\n";  // 20  — at()
v[0] = 99;                 // writing works too

The difference is bounds checking. v.at(5) on a 3-element vector throws a std::out_of_range exception that you can catch. v[5] does no checking at all — it is undefined behaviour, which may crash, may print garbage, or may seem to "work" while quietly corrupting data.

Rule of thumb: use [] in tight loops where you have already checked the range, and at() when the index comes from user input or you want a clear error instead of a silent bug.

Size vs capacity

These two look similar but mean different things, and confusing them trips up many learners.

  • size() — how many elements are actually stored right now.
  • capacity() — how many elements the vector can hold before it must grab more memory.

A vector keeps a block of memory that is often bigger than it currently needs. When you push_back and there is no room left, it allocates a larger block (commonly about double the size) and copies the old elements over. That is why capacity() tends to grow in jumps like 1, 2, 4, 8, 16.

vector<int> v;
for (int i = 0; i < 5; i++) {
    v.push_back(i);
    cout << "size=" << v.size()
         << " capacity=" << v.capacity() << "\n";
}

If you know roughly how many items you will add, call v.reserve(1000) first. This sets the capacity up front and avoids repeated re-allocations, which makes the loop faster. Note that reserve changes capacity, not size.

Looping over a vector

The cleanest way to visit every element is the range-based for loop:

vector<int> v = {1, 2, 3, 4};
for (int x : v)
    cout << x << " ";        // 1 2 3 4

Here x is a copy of each element. To change the elements in place, take a reference with &. To read large objects without copying, use const auto&:

for (int &x : v) x *= 2;            // doubles each element
for (const auto &x : v) cout << x; // read-only, no copy

When you need the index, loop with a counter. Use size_t to match the type that size() returns:

for (size_t i = 0; i < v.size(); i++)
    cout << i << ": " << v[i] << "\n";

The classic low-level option is iterators, which many STL algorithms expect:

for (auto it = v.begin(); it != v.end(); ++it)
    cout << *it << " ";   // *it reads the element

2D vectors (a grid)

A 2D vector is simply a vector whose elements are themselves vectors — perfect for grids, matrices, and game boards. The type is vector<vector<int>>.

// 3 rows, 4 columns, every cell 0
vector<vector<int>> grid(3, vector<int>(4, 0));

grid[0][0] = 5;
grid[1][2] = 9;

for (const auto &row : grid) {
    for (int val : row)
        cout << val << " ";
    cout << "\n";
}

Read the constructor as "3 copies of a row, where each row is 4 zeros". Unlike a fixed 2D array, each row can even be a different length (a "jagged" grid) if you build it with push_back. For most competitive-programming and matrix tasks, this one line replaces a lot of manual memory work.

Vector vs raw array

Raw arrays still have their place — small, fixed, performance-critical buffers — but for everyday code the vector wins on safety and convenience.

Featurestd::vectorRaw array
Resizes at runtimeYesNo
Knows its own sizeYesNo
Bounds-checked accessWith at()No
Manages its own memoryYesNo
Works cleanly with STL algorithmsYesPartial
Zero overheadAlmostYes

The recommendation is simple: reach for std::vector by default, and only drop down to a raw array when you have a measured reason. If you want a guided path from the basics through STL containers like this, our free C++ course walks through it step by step.

Frequently Asked Questions

What is the difference between a C++ vector and an array?

A raw array has a fixed size set at compile time and does not know its own length. A std::vector is a dynamic array that resizes at runtime, tracks its length with size(), manages its own memory, and offers helpers like push_back() and at(). For most code, prefer a vector.

Is v[i] or v.at(i) faster in C++?

v[i] is slightly faster because it skips bounds checking, while v.at(i) checks the index and throws std::out_of_range if it is invalid. In hot loops where you have already validated the range, use []. When the index comes from outside your control, use at() so a bug becomes a clear exception instead of undefined behaviour.

How do I get the number of elements in a vector?

Call v.size(), which returns the current element count. To check whether a vector is empty, v.empty() is clearer and returns a bool. Do not confuse size() with capacity() — capacity is how much memory is reserved, which can be larger than the size.

Can a vector hold strings or my own objects?

Yes. A vector can hold any type: vector<string>, vector<double>, even vector<Student> for your own class. The only requirement is that the type can be copied or moved. For your own objects, emplace_back() can build them directly inside the vector.

How do I remove elements from a vector?

Use pop_back() to drop the last element, clear() to remove everything, or v.erase(v.begin() + i) to remove the element at index i. Erasing from the middle shifts the following elements down, so it is O(n); if order does not matter, swapping with the last element and then calling pop_back() is faster.