Step 1 of 5 · Reading · ~3 min
Learn
STL Collections
std::vector
A std::vector is a list of values that grows as you add to it and always knows its own length. By the end of this lesson you can fill a vector from input of unknown length, walk it more than once, and find an extreme value without the classic seeding bug.
Creating and growing
#include <vector>
std::vector<double> prices; // empty: size() is 0
prices.push_back(4.5); // append at the back
prices.push_back(12.0);
std::vector<int> days = {31, 28, 31}; // literal initialisation
std::vector<int> slots(4, -1); // four copies of -1: (count, value)
prices.size() // 2
days.back() // 31, the last element
slots.empty() // false
The <double> in angle brackets is a template argument: it fixes the element type, so a std::vector<int> refuses a string at compile time. push_back appends one element; the vector owns a heap buffer, enlarges it when full, and frees it automatically when the vector goes out of scope. No malloc, no free, no fixed capacity to guess.
Reading input of unknown length
When the input has no count up front, let the stream be the loop condition. std::cin >> t evaluates to false once there is nothing left to read:
std::vector<int> temps;
int t;
while (std::cin >> t) temps.push_back(t);
For the input 18 -4 7 this runs three times, and temps ends as {18, -4, 7} with temps.size() == 3. A single std::cin >> t with no loop reads only the first value.
Access and iteration
temps[1] // -4: fast, unchecked; a bad index is undefined behaviour
temps.at(1) // -4: checked; a bad index throws std::out_of_range
Same two doors as std::string. Walk the vector with the range-for you already know, or with an index when the position matters. std::size_t is the unsigned type size() returns, so an index of that type compares without a warning:
for (const auto& t : temps) std::cout << t << ' '; // 18 -4 7
for (std::size_t i = 0; i < temps.size(); ++i) { /* temps[i] */ }
Why store the values at all
Some questions need two passes: you cannot know which values are above the average until you have seen all of them. A vector lets you read once and walk twice:
std::vector<int> scores = {70, 90, 80, 100};
long long total = 0;
for (int s : scores) total += s; // pass 1: 340
double avg = static_cast<double>(total) / scores.size(); // 85.0
int above = 0;
for (int s : scores) if (s > avg) above += 1; // pass 2: 90 and 100
std::cout << above << "\n"; // 2
Tracking an extreme: seed from the data
To find the smallest temperature, start the tracker at the first element, not at a made-up number:
int coldest = temps[0]; // 18
for (const auto& t : temps) {
if (t < coldest) coldest = t; // 18 -> -4, then 7 is not smaller
}
std::cout << coldest << "\n"; // -4
Seed coldest with 0 instead and input 18 25 7 prints 0, a value that is not in the list. The same trap flips for a maximum: seeding with 0 on all-negative input prints 0.
Common mistakes
- Seeding the tracker with 0. On
-3 -1 -7 -1the largest is-1; a zero seed prints0and then counts zero matches. - Stopping one short. A loop bounded by
i < v.size() - 1never looks at the last element, so1 2 3 4 10reports4. - Counting while still searching. If you count matches in the same pass and forget to reset the count when a bigger value appears,
5 5 9 9 9still carries the two matches of5and reports a count above3. - Reading one number. Without a loop around
std::cin >>, only the first value is ever seen.
Your exercise
Read a line of integers into a vector, then print the largest value and how many times it occurs. The starter declares the vector and does the printing; you write the reading loop and the search. Seed from the data, not from zero.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…