Video Coming Soon...

Created by Zed A. Shaw Updated 2024-10-28 08:02:26

16: Basic Containers

You've been using simple data types like integers (int) and strings (string) so now it's time you learn about containers. A container is a piece of code that knows how to hold multiple similar pieces of data into a structure. The structure can be something like a list, a key=value store, or even a matrix that's similar to an Excel spreadsheet. Programmers typically call these structures by the incredibly creative name of "data structures"...because they...structure...the data.

Really, any time you think programmers are insane geniuses just remember they're regular people too who will name things fairly closely to what they actually do. This may help you avoid overthinking solutions in the future.

Introducing vector

In this exercise we'll learn a single data structure called the vector. A "vector" is simply a fancy name for an ordered (but not sorted) list of things. I think the name was chosen mostly to make it sound more fancy than it actually is. You put things in it, find those things, take things out, just like you might a list of names for a party.

The "things" you can put in a vector are any data type that C++ can identify and knows its size. That means, string, int, and even other another vector, but don't do that yet.

Code Example

Here is an example of using a vector to store a list of int numbers.

View Source file ex16.cpp Only

#include <fmt/core.h>
#include <vector>

using namespace fmt;
using std::vector;

int main() {
  vector<int> ages = {10,34,25,19,87,5};

  println("I have {} cousins of different ages:", ages.size());

  size_t i = 0; // initializer
  while(i < ages.size()) { // test
    int cousin_age = ages.at(i);
    println("cousin #{} is {} years old.", i, cousin_age);
    ++i; // incrementer
  }
}

I want you to get this working and walk through the code with me. Then you're going to complete a series of challenges to learn more.

Code Walkthrough

Instead of doing each line by number I'm going to instead talk about pieces of code directly. Everything else you already know, so I'm going to stop holding your hand and make you study the code yourself more and more.

#include <vector>
This is how you tell C++ you want to use vectors in your code.
using std::vector;
If you don't do this then you have to type std::vector all the time so C++ knows where the vector is. Remember that the std is a namespace that holds a lot of built-in C++ modules.
vector<int> ages = {10,34,25,19,87,5};
This creates the list of your cousins' ages. You first state you want a vector, then you have to say what type this vector holds. The <int> is how you indicate the type. Next you give it a name ages and set it equal to the list of ages. The {} syntax may look like your code blocks, but when you see it used like this it is an initializer and it will be used in C++ a lot. Finally, your list of numbers has each number separated by a , (comma).
println("I have {} cousins of different ages:", ages.size());
Now for the best part, you can "ask" ages how big it is (how many int it holds) by typing age a . and the function name size(). You already now about functions so this is a new style that is kind of like doing size(ages) but targeted directly at ages. You'll learn more about this when you learn about Object Oriented Programming but think about this like "asking ages do to something" or "sending ages the size() command."
size_t
This isn't a line, but just this one keyword. A size_t is a special integer that is only positive and typically used to index into data structures. We use this instead of an int because you can't ask ages for negative indexes.
int cousin_age = ages.at(i);
You should know how your while loop works already, but this one line is new. The int cousing_age = part isn't new. What is new is the ages.at(i). This does pretty much what it says, "ages, give me the int at index i." It is probably self-explanatory but you'll notice when you run this code that i is going to go from 0 to 5 rather than what you would expect of 1 to 6.

That should be all the new stuff. If there's something you don't understand be sure to figure out how you missed it and study this as much as possible. Definitely, definitely play with this code before you continue.

Why 0 Indexing

Humans normally think about lists of things in order. If you think of dates on a calendar we have "first", "second", "thirty-first", and so on. If you watch people run a marathon you'd ask things like, "Who came in first place?" This use of the "st" and "nd" in words like "first" and "second" tells me that this is an "ordered" list. I can't have "second place" without having "first place." I can't have the "second of the month" without having the "first of the month."

A vector isn't a strictly ordered list though. You can grab elements from it at any point. If you want the 4th cousin's age you just write vector.at(4) so there really isn't a "first" element. Instead we have to say, "I want the cousin at index 3" to get the 4th cousin.

This is different from the days of a month because you can't time travel and jump forward or backward in time to any random date you want. Dates are strictly ordered, and that means they use an ordered numbering scheme starting at 1. Random structures like vector start with 0 because to make random access math work right you need to start at 0. All kinds of indexing math doesn't work right if you start at 1. Some languages like Lua do start at one, and they are annoying to deal with when you need random indexing math.

So, the way you translate from "human who lives in a world with time and only deals with strictly ordered things" to a "human who must pick a random thing out of a vector" is you use this formula:

Ask what ordered one you want, then subtract one to get the random index.

This means if you say, "I want the FOURTH cousin's age." Then you have an ordered number 4, so subtract 1 and you get 3. You want index 3, so ages.at(3).

Challenge Time

I will now give you a series of challenges to accomplish using this code. You should be able to do all of these with only what you know so far, but if you get stuck jump to the end and read the documentation for vector mentioned in Further Study.

  1. Make a vector of string that holds your cousins names. If you don't have that many cousins jsut use the names of fruits and veggies.
  2. In the while-loop print both the cousin's name and their age using only i for the index to both vectors.
  3. When you index into a vector you start at 0 so that certain math works, but most humans find this weird. Change your println() to make it more human without breaking your i index.
  4. Add up all the ages for all your cousins then print the total and average ages after the loop.
  5. After the loop print one line for the 4th cousin, then 2nd cousin, and the 6th cousin. Remember your "human ordered to random index" math.

Breaking It

Breaking this code is super easy:

  1. Instead of using ages.size() use a fixed number, then change the ages vector to have fewer contents.
  2. Give ages.at() a negative number.
  3. Give ages.at() a way bigger number.
  4. Remove the ++i incrementer and watch the world burn.
  5. Give fewer names than ages in your modified challenge code.
  6. Get rid of the using std::vector to see what C++ says about your mistake.

Get creative. There's many ways to break this, and in Further Study you'll get even more by reading the documentation. Go crazy, because breaking your own code is how you get familiar with errors and reduce your fear of making mistakes.

Further Study

Previous Lesson Next Lesson

Register for Learn C++ the Hard Way

Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.