This article explains what vectors are, why they matter, and how to use them confidently—with crystal‑clear code, outputs, and “Try it yourself” challenges after each section for hands‑on learning.
🔹 What is a Vector in C++?
A Vector in C++ is a dynamic array from the Standard Template Library (STL) that automatically grows and shrinks as elements are added or removed. Unlike fixed‑size arrays, vectors manage memory for you and provide a clean, consistent interface for safe, readable code.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> nums; // empty vector
nums.push_back(10); // add one element
nums.push_back(20); // add another
cout << "Size: " << nums.size() << "\n";
cout << "First: " << nums << "\n";
}
Output
Size: 2
First: 10
Try it yourself
- Add another element and print
back()
to show the last value. - Replace
nums
withnums.at(0)
and trynums.at(5)
to see the bounds‑check error message in action.
🔹 Why use Vector in C++?
- Flexible: Grows and shrinks as needed—no manual resizing.
- Safe access:
at()
checks bounds;[]
is fast but unchecked. - STL‑friendly: Works with algorithms, iterators, and range‑for loops.
- Performance: Amortized constant‑time appends via capacity doubling; contiguous memory for cache‑friendly access.
🔹 Creating and Initializing a Vector in C++
Here are common ways to create and initialize a Vector in C++, from empty to pre‑filled containers.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> a; // empty
vector<int> b(5); // size 5, default-initialized (0 for int)
vector<int> c(3, 42); // size 3, all elements 42
vector<int> d{1, 2, 3, 4}; // initializer list
vector<string> names{"Ava", "Noah"}; // vector of strings
cout << "c size: " << c.size() << "\n";
cout << "d: " << d << "\n";
cout << "names: " << names << "\n";
}
Output
c size: 3
d: 3[10]
names: Ava
Try it yourself
- Create
vector<double> prices(4, 9.99)
and print all elements. - Use
vector<int> e = d
to copy and then modifye
; verifyd
is unchanged (deep copy).
🔹 Adding, Accessing, and Removing Elements
Use push_back
and emplace_back
to append, front/back
to peek ends, and pop_back
to remove from the end. Prefer at()
for safe indexed reads in beginner code.
#include <vector>
#include <iostream>
#include <string>
using namespace std;
int main() {
vector<string> todo;
todo.push_back("learn vector"); // copy string
todo.emplace_back("practice push"); // construct in place
cout << "First: " << todo.front() << "\n";
cout << "Last : " << todo.back() << "\n";
todo.pop_back(); // remove last
cout << "Size after pop: " << todo.size() << "\n";
// Safe access
if (!todo.empty()) {
cout << "Item 0: " << todo.at(0) << "\n";
}
}
Output
First: learn vector
Last : practice push
Size after pop: 1
Item 0: learn vector
Try it yourself
- Replace
push_back("learn vector")
withstring s = "learn vector"; todo.push_back(s)
and explain copy vs in‑place construction. - Call
todo.clear()
and checkempty()
andsize()
.
🔹 Iterating a Vector in C++
Iterate using range‑for for simplicity, classic indices for random access, or iterators for STL algorithms.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v{2, 4, 6, 8};
// Range-for (read-only)
for (int x : v) {
cout << x << " ";
}
cout << "\n";
// Index-based (read/write)
for (size_t i = 0; i < v.size(); ++i) {
v[i] += 1;
}
// Iterator-based
for (auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
cout << "\n";
}
Output
2 4 6 8
3 5 7 9
Try it yourself
- Use
const auto&
in range‑for to avoid copies:for (const auto& x : v)
. - Print elements in reverse using
rbegin()
/rend()
.
🔹 Size, Capacity, reserve, and shrink_to_fit
A Vector in C++ keeps a logical size (number of elements) and a physical capacity (allocated slots). Appending beyond capacity triggers a reallocation. Use reserve(n)
to pre‑allocate and reduce reallocations; shrink_to_fit()
requests trimming extra capacity.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v;
cout << "size=" << v.size() << ", cap=" << v.capacity() << "\n";
v.reserve(100); // pre-allocate
cout << "after reserve cap=" << v.capacity() << "\n";
for (int i = 0; i < 50; ++i) {
v.push_back(i);
}
cout << "after push size=" << v.size() << ", cap=" << v.capacity() << "\n";
v.shrink_to_fit(); // request trim
cout << "after shrink cap=" << v.capacity() << "\n";
}
Output
size=0, cap=0
after reserve cap=100
after push size=50, cap=100
after shrink cap=50
Try it yourself
- Remove
reserve(100)
and print capacity growth as you push elements (notice how it expands). - Call
v.resize(10)
to shrink size without changing capacity; print both to compare.
🔹 Inserting and Erasing Anywhere
Use insert
/emplace
to add elements at a position and erase
to remove by iterator or range. Note that inserting/erasing in the middle can move elements and may invalidate iterators/pointers/references.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v{1, 2, 3, 4, 5};
v.insert(v.begin() + 1, 99); // insert 99 at index 1
v.emplace(v.end(), 77); // emplace 77 at the end
v.erase(v.begin() + 2); // remove the '2'
v.erase(v.begin() + 2, v.begin() + 4); // remove a range: 3 and 4
for (int x : v) {
cout << x << " ";
}
cout << "\n";
}
Output
1 99 5 77
Try it yourself
- Insert three 0s at position 1 using
v.insert(v.begin() + 1, 3, 0)
. - Erase all even numbers using a loop with iterators (be careful to update the iterator after erase).
🔹 2D Vector in C++ (Vector of Vectors)
A Vector in C++ can itself store other vectors, which is a clean way to make dynamic 2D (or ND) grids without manual memory management.
#include <vector>
#include <iostream>
using namespace std;
int main() {
int rows = 3;
int cols = 4;
vector<vector<int>> grid(rows, vector<int>(cols, 0));
for (const auto& row : grid) {
for (int x : row) {
cout << x << " ";
}
cout << "\n";
}
}
Output
0 0 0 0
0 0 0 0
0 0 0 0
Try it yourself
- Resize to 5×5 using
grid.assign(5, vector<int>(5, -1))
and set a diagonal. - Build an adjacency list:
vector<vector<int>> adj(n)
and push neighbors for each node.
🔹 Interop with Algorithms (sort, find, accumulate)
Vectors play perfectly with standard algorithms for searching, sorting, and aggregating, producing clear and efficient code.
#include <vector>
#include <iostream>
#include <algorithm>
#include <numeric>
using namespace std;
int main() {
vector<int> v{5, 1, 4, 2, 3};
sort(v.begin(), v.end());
auto it = find(v.begin(), v.end(), 4);
bool found = (it != v.end());
int sum = accumulate(v.begin(), v.end(), 0);
cout << "sorted: ";
for (int x : v) {
cout << x << " ";
}
cout << "\n";
cout << "found 4? " << boolalpha << found << "\n";
cout << "sum: " << sum << "\n";
}
Output
sorted: 1 2 3 4 5
found 4? true
sum: 15
Try it yourself
- Remove duplicates using
sort
+unique
+erase
. - Partition odds and evens with
stable_partition
and print both groups.
🔹 Best Practices for Vector in C++
- Prefer
vector
over raw arrays for safety, clarity, and STL compatibility. - Use
reserve
when the final size is known to reduce reallocations. - Pass large vectors by
const&
to avoid copies; return by value (RVO/move) is fine. - Use
emplace_back
for objects to avoid extra copies when constructing in place. - Check
empty()
beforefront()
/back()
, and preferat()
in teaching/beginner code.
🔹 Common Pitfalls and How to Avoid Them
- Invalidated references: Reallocation can move elements, invalidating pointers/iterators/references—avoid storing them across
push_back
/insert
withoutreserve
. - Out‑of‑range access:
[]
is unchecked—useat()
when unsure. - Capacity confusion:
resize
changes size (constructs/destroys elements);reserve
changes only capacity. - Frequent mid‑vector edits: Insert/erase at the front/middle shifts elements—consider
deque
/linked structures if this dominates. - vector<bool>: It’s a space‑optimized specialization with proxy elements; prefer
vector<char>
/vector<uint8_t>
if you need real references.
🔹 Mini Project: Scoreboard with Vector
This mini project uses a Vector in C++ to track scores: add, list, sort, and compute stats.
#include <vector>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <iomanip>
using namespace std;
int main() {
vector<int> scores;
scores.push_back(88);
scores.push_back(92);
scores.push_back(75);
scores.push_back(96);
scores.push_back(83);
cout << "Scores: ";
for (int s : scores) {
cout << s << " ";
}
cout << "\n";
sort(scores.begin(), scores.end());
double avg = accumulate(scores.begin(), scores.end(), 0.0) / scores.size();
cout << "Sorted: ";
for (int s : scores) {
cout << s << " ";
}
cout << "\n";
cout << fixed << setprecision(2);
cout << "Average: " << avg << "\n";
cout << "Best: " << scores.back() << "\n";
cout << "Worst: " << scores.front() << "\n";
}
Output
Scores: 88 92 75 96 83
Sorted: 75 83 88 92 96
Average: 86.80
Best: 96
Worst: 75
Try it yourself
- Add
erase
to remove any score < 80, then recompute average. - Read scores from input until EOF and handle empty input safely.
🔹 FAQ: Vector in C++
Q1. What’s the difference between size()
and capacity()
?
size()
is the number of elements currently stored; capacity()
is how many could be stored before reallocating memory.
Q2. When should I use reserve()
?
Use reserve()
when the final number of elements is known (or roughly known) to avoid repeated reallocations and iterator invalidation.
Q3. Is vector
contiguous in memory?
Yes. Elements are stored contiguously, which is great for cache performance and interop with C APIs via data()
.
Q4. Should I use []
or at()
?
[]
is unchecked and fast; at()
throws on out‑of‑range. For teaching and debugging, prefer at()
; switch to []
when bounds are guaranteed.
Q5. Why did my iterators become invalid?
Any operation that reallocates (e.g., growth beyond capacity, inserts) can move elements, invalidating pointers, references, and iterators. Call reserve()
or reacquire iterators after modification.
Q6. What’s the complexity of push_back
?
Amortized constant time. Occasional growth operations cost more, but averaged over many inserts, the cost per push is O(1).
Q7. Is vector<bool>
safe to use?
It’s specialized to pack bits and returns proxy objects; most beginners should prefer vector<char>
or vector<uint8_t>
for boolean flags to avoid surprises.
Q8. How do I make a 2D dynamic array?
Use vector<vector<T>>
for simplicity, or a single vector<T>
with manual indexing if you need strict contiguity across rows.
🔹 Wrapping Up
Mastering Vector in C++ unlocks clean, safe, and high‑performance data handling. Prefer vectors over raw arrays, use reserve()
to control reallocations, iterate with range‑for for clarity, and lean on STL algorithms for powerful one‑liners. Don’t forget the “Try it yourself” challenges above to turn knowledge into muscle memory.