vector: a row of boxes
An int variable holds one number. To store a class's scores or a shopping list, use std::vector: a row of boxes side by side, and the number of boxes can change. To use it, #include <vector>.
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> scores{90, 85, 77};6 std::cout << "First: " << scores[0] << '\n';7 std::cout << "Count: " << scores.size() << '\n';8 return 0;9}
First: 90 Count: 3
In std::vector<int>, the int in angle brackets says each box holds an int. The values in the boxes are the elements. scores[0] is the first element; the number in square brackets is the index, and it starts at 0. size() tells you how many elements there are now. Press Step and see scores drawn as a row of three boxes.
Making a vector
The usual ways to write it:
| You write | You get |
|---|---|
std::vector<int> v; | empty, no elements at all |
std::vector<int> v{4, 8, 15}; | three elements: 4, 8, 15 |
std::vector<int> v(5, 0); | five elements, all 0 |
std::vector<std::string> v; | empty, for strings later |
Curly braces and parentheses are different: curly braces list the elements themselves; parentheses say "how many, and what each one is".
What does this show?
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> a(3, 7);6 std::vector<int> b{3, 7};7 std::cout << a.size() << " " << b.size() << '\n';8 return 0;9}
a(3, 7) is three 7s, so 3 elements. b{3, 7} is just the two elements 3 and 7, so 2.
push_back: add to the end
push_back adds a new element at the end, and the vector grows one more box by itself. Press Step and watch the boxes fill one by one:
1#include <iostream>2#include <string>3#include <vector>4 5int main() {6 std::vector<std::string> list;7 std::cout << "Start: " << list.size() << " items\n";8 list.push_back("milk");9 list.push_back("eggs");10 list.push_back("bread");11 std::cout << "Now: " << list.size() << " items\n";12 std::cout << "Last one: " << list[2] << '\n';13 return 0;14}
Start: 0 items Now: 3 items Last one: bread
You don't have to say up front how many boxes you need: start with an empty one and add things as they come. That is the most common way to use a vector.
Indexes and loops
v[i] reads an element, and can change it too. To go through every element, let the index run from 0 up to size() - 1:
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> scores{90, 85, 77, 68};6 scores[1] = 88;7 int total = 0;8 for (std::size_t i = 0; i < scores.size(); i++) {9 std::cout << i << ": " << scores[i] << '\n';10 total += scores[i];11 }12 std::cout << "Average: " << static_cast<double>(total) / scores.size() << '\n';13 return 0;14}
0: 90 1: 88 2: 77 3: 68 Average: 80.75
The index i is a std::size_t: an integer type that is never negative, made for sizes and indexes, and the type size() gives you.
Note: Because size() is never negative, size() - 1 on an empty vector is not -1 but a huge number. Write i < v.size(), not i <= v.size() - 1.
Going out of range
The vector below has only three elements, so the index can only be 0, 1 or 2. What happens when you read prices[5]?
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> prices{12, 30, 8};6 std::cout << "First: " << prices[0] << '\n';7 std::cout << "Sixth: " << prices[5] << '\n';8 return 0;9}
First: 12
Sixth:
index out of rangeWhat happened: Stopped at line 7. This goes past the end of a vector, an array or a string. Real C++ would read or overwrite whatever is next in memory.
Square brackets don't check the index. With a real compiler this is undefined behavior: it may show a strange number, it may crash, or it may look like nothing went wrong, which is the most dangerous case. Our engine stops on that line and tells you where it went out of range.
at(i) gets an element just like [i], but checks the index first. If the index is wrong, it throws a std::out_of_range exception; when nothing handles the exception, the program ends right there:
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> prices{12, 30, 8};6 int sixth = prices.at(5);7 std::cout << "Sixth: " << sixth << '\n';8 std::cout << "This line never runs either\n";9 return 0;10}
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
Aborted (core dumped)The program ends on the at(5) line, and nothing after it runs. The terminate called after throwing an instance of 'std::out_of_range' and Aborted (core dumped) on the page are what a g++ program shows on your own computer too; the what(): line in between says exactly what happened: index 5 (__n), but only 3 elements (size()). Lesson 26 shows how to catch an exception and keep the program going.
Tip: When you aren't sure an index is right, use at(): a mistake shows up at once instead of quietly reading a strange number.
First and last
| You write | What it does |
|---|---|
v.front() | the first element, same as v[0] |
v.back() | the last element, same as v[v.size() - 1] |
v.pop_back() | removes the last element |
v.empty() | true when there are no elements |
1#include <iostream>2#include <string>3#include <vector>4 5int main() {6 std::vector<std::string> todo{"groceries", "electric bill", "laundry"};7 std::cout << todo.front() << ", " << todo.back() << '\n';8 todo.pop_back();9 std::cout << todo.size() << " left, the last is " << todo.back() << '\n';10 while (!todo.empty()) {11 todo.pop_back();12 }13 std::cout << todo.size() << " left\n";14 return 0;15}
groceries, laundry 2 left, the last is electric bill 0 left
while (!todo.empty()) means "while there is something left". An empty vector has no first or last element: front(), back() or pop_back() on it is undefined behavior, so check empty() first.
Copying and passing to functions
Assigning one vector to another copies all the elements. After that, each one is on its own:
What does this show?
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> a{1, 2, 3};6 std::vector<int> b = a;7 b[0] = 100;8 std::cout << a[0] << " " << b[0] << '\n';9 return 0;10}
b = a copied all the elements, so b is a separate row of boxes. Changing b[0] doesn't touch a.
Parameters work the same way: a plain std::vector<int> parameter copies the whole vector. Just like strings in lesson 10, pass const std::vector<int>& to only read it, and std::vector<int>& to change it:
1#include <iostream>2#include <string>3#include <vector>4 5int total_price(const std::vector<int>& prices) {6 int total = 0;7 for (std::size_t i = 0; i < prices.size(); i++) {8 total += prices[i];9 }10 return total;11}12 13void add_item(std::vector<int>& prices, int price) {14 prices.push_back(price);15}16 17int main() {18 std::vector<int> cart{12, 30};19 add_item(cart, 8);20 std::cout << cart.size() << " items, $" << total_price(cart) << " in all\n";21 return 0;22}
3 items, $50 in all
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
A week of steps
✓steps holds the step counts for seven days. Add them up with a loop and show Total steps: 51300.
Total steps: 51300
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> steps{6200, 8100, 7500, 9000, 5400, 10300, 4800};6 7 return 0;8}
A numbered shopping list
✓Start with an empty list, push_back rice, oil and salt in that order, then use a loop to show the list numbered from 1.
1. rice 2. oil 3. salt
1#include <iostream>2#include <string>3#include <vector>4 5int main() {6 std::vector<std::string> list;7 8 return 0;9}
Fix it: the last one, off by one
✓This program wants to show the last price but goes out of range. Fix it so it shows Last: 45.
Last: 45
1#include <iostream>2#include <vector>3 4int main() {5 std::vector<int> prices{20, 35, 45};6 std::cout << "Last: " << prices[prices.size()] << '\n';7 return 0;8}
What you learned
std::vector<type>is a row of boxes and needs#include <vector>. Curly braces list the elements;(n, x)is n copies of x.push_backadds to the end andpop_backremoves the last one;size()is the number of elements andempty()tells whether there are none.v[i]reads and changes an element; the index runs from 0 tosize() - 1, so loop withi < v.size().v[i]out of range is undefined behavior;v.at(i)out of range throwsstd::out_of_rangeand the program ends.front()andback()are the first and last elements; don't use them on an emptyvector.- Assigning copies the whole
vector; pass it to functions asconst std::vector<int>&(to read) orstd::vector<int>&(to change).