Vector in C++ STL
In this lesson: Use std::vector in C++ — push_back, size, insert, erase, capacity — with examples and output. In Hindi & English.
What Is std::vector in C++?
std::vector<T> is the standard growable sequence container. For ordinary element types, it stores elements contiguously like an array, but manages storage and lifetime automatically. Its size can change at runtime.
#include <vector>
std::vector<int> marks{78, 91, 84};| Property | Vector guarantee |
|---|---|
| Indexed access | Constant time |
| Insert/erase at end | Amortized constant time for insertion |
| Insert/erase in middle | Linear time |
| Storage | Contiguous for T other than bool |
| Ownership | Elements and storage released automatically |
Use std::array for a fixed compile-time size and std::deque when efficient operations at both ends are essential. For the wider decision, see vector versus map.
Creating and Initializing Vectors
std::vector<int> empty; // size 0
std::vector<int> five_zeros(5); // 0 0 0 0 0
std::vector<int> four_sevens(4, 7); // 7 7 7 7
std::vector<int> values{5, 10, 15}; // initializer list
std::vector copy = values; // class template deduction
std::vector<int> part(values.begin(), values.begin() + 2);Parentheses and braces mean different things. vector<int>(5, 10) contains five copies of 10, while vector<int>{5, 10} contains two elements: 5 and 10.
A vector owns values, not references. Store a lightweight record type rather than parallel vectors that can become misaligned:
struct Student {
std::string name;
int mark;
};
std::vector<Student> students{{"Aman", 78}, {"Sara", 91}};Include every directly used header, here <vector> and <string>. Avoid using namespace std; in headers and teaching examples.
Safe Element Access
| Operation | Use | Empty/out-of-range behavior |
|---|---|---|
v[i] | Known-valid index | No bounds check |
v.at(i) | Checked index | Throws std::out_of_range |
v.front() | First element | Requires non-empty vector |
v.back() | Last element | Requires non-empty vector |
v.data() | Pointer to contiguous range | Do not dereference for empty range |
if (!marks.empty()) {
std::cout << marks.front() << ' ' << marks.back();
}
try {
std::cout << marks.at(user_index);
} catch (const std::out_of_range&) {
std::cerr << "Invalid index\n";
}Use std::size_t or an appropriate container size type for indexes. When reverse loops are needed, avoid unsigned underflow; reverse iterators are often clearer.
Essential Vector Methods
| Method | Effect |
|---|---|
push_back(value) | Copy/move a value to the end |
emplace_back(args...) | Construct an element at the end |
pop_back() | Remove last element; vector must not be empty |
insert(pos, value) | Insert before an iterator position |
erase(pos/range) | Remove one element or a range |
clear() | Destroy all elements; capacity need not shrink |
assign(...) | Replace the contents |
swap(other) | Exchange contents and capacity |
marks.push_back(96);
marks.emplace_back(88);
marks.insert(marks.begin() + 1, 80);
marks.erase(marks.begin() + 2);
// C++20: remove every failing mark and return count removed
const auto removed = std::erase_if(marks, [](int m) { return m < 40; });pop_back() returns nothing. Read back() first if you need the value. Prefer emplace_back when constructor arguments are available, but do not assume it is automatically faster than moving an already-created object.
Size, Capacity, reserve and resize
std::vector<int> values;
values.reserve(100); // capacity at least 100, size still 0
values.push_back(7); // size becomes 1
values.resize(4); // size 4: appends three value-initialized zeros| Member | Meaning |
|---|---|
size() | Number of live elements |
capacity() | Elements current storage can hold before reallocation |
reserve(n) | Request capacity of at least n; size unchanged |
resize(n) | Change number of elements |
shrink_to_fit() | Non-binding request to reduce capacity |
reserve(100), values[50] is invalid because capacity is not size. Add elements or resize before indexing.Reserve when a reliable approximate final size is known. Calling reserve before every push_back can defeat geometric growth and worsen performance. Growth factor is implementation-specific; write against the capacity guarantee, not an assumed doubling rule.
Iteration, Algorithms and Invalidation
for (int mark : marks) { // copy each int
std::cout << mark << ' ';
}
for (const Student& student : students) { // no record copy
std::cout << student.name << '\n';
}
std::sort(marks.begin(), marks.end());Vector iterators are random-access and contiguous iterators, so standard algorithms such as sort, binary_search and lower_bound work efficiently when their preconditions are met.
- Reallocation invalidates every iterator, pointer and reference to elements, including the past-the-end iterator.
- If insertion does not reallocate, handles before the insertion position remain valid; those at or after it are invalidated.
- Erase invalidates handles at or after the erased position.
reserveinvalidates handles only if it reallocates.
auto it = marks.begin();
marks.push_back(99); // it may now be invalid
it = marks.begin(); // reacquire before useContinue with iterators and STL algorithms.
Complete Practical Program with Verified Output
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> marks;
marks.reserve(6);
for (int mark : {72, 88, 65, 91, 88}) {
marks.push_back(mark);
}
const int total = std::accumulate(marks.begin(), marks.end(), 0);
const auto highest = *std::max_element(marks.begin(), marks.end());
std::sort(marks.begin(), marks.end());
std::cout << "Sorted:";
for (int mark : marks) std::cout << ' ' << mark;
std::cout << std::fixed << std::setprecision(1)
<< "\nAverage: " << total / static_cast<double>(marks.size())
<< "\nHighest: " << highest;
}The vector owns five integers. Reserving six positions avoids reallocation during these insertions but creates no elements. The average uses floating-point division; integer division would incorrectly discard the decimal part.
Common Mistakes, Practice and Review
| Mistake | Correction |
|---|---|
Index equals size() | Last valid index is size()-1 for non-empty vector |
| Indexing after reserve only | push/resize first |
| Calling front/back/pop on empty vector | Check empty() |
| Keeping iterator across growth | Reacquire after possible reallocation |
| Expecting clear to free capacity | Capacity may remain for reuse |
| Using vector of raw owning pointers | Store values or owning smart pointers |
| Parallel vectors for one record | Use vector<Record> |
Practice: (1) remove duplicate marks using sort–unique–erase, (2) store Student records and sort by descending mark, (3) demonstrate size/capacity before and after reserve, (4) erase all negative values safely, and (5) compare checked at() with [].
- Did you include
<vector>directly? - Is the index provably valid?
- Are empty-container preconditions handled?
- Is capacity being confused with size?
- Could mutation invalidate a saved handle?
- Are algorithms given valid ranges and preconditions?
Authoritative References
- C++ Working Draft: std::vector
- C++ Working Draft: Sequence Containers
- C++ Working Draft: Container Requirements
Storage, complexity, reserve/resize and invalidation statements were checked against the current working draft. For STL architecture, read the complete STL guide.
