Skip to content
Lesson 10 of 15

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 -1 the largest is -1; a zero seed prints 0 and then counts zero matches.
  • Stopping one short. A loop bounded by i < v.size() - 1 never looks at the last element, so 1 2 3 4 10 reports 4.
  • 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 9 still carries the two matches of 5 and reports a count above 3.
  • 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.

Up nextstd::map and std::unordered_mapSTL Collections

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…