1class Tally {2 int n;3public:4 Tally(int s) : n{s} {}5 void add(int k) { n += k; }6 int get() const { return n; }7 ~Tally() {8 std::cout << "bye " << n << '\n';9 }10};$ ./tally8# what if: add t.n = 9;$ g++ tally.cpp# error: n is privatebye 1bye 81int main() {2 Tally t{5};3 t.add(3);4 std::cout << t.get() << '\n';5 {6 Tally u{1};7 }8}t.ntu.nu5+381bye 1bye 8!
Violet cards: the code (the class Tally and main)Slate boxes: the objects t and u in memory, faint before they are made and after they are destroyedTokens: { } a call into the class, khaki values moving, a red ! for a compile errorLeft: the class card, class Tally, the blueprint. Right: the main() card that uses it. At the back: a memory box for each object, t and u, and the terminal. Each line of main sends a call to the member it runs: the constructor fills a new box, add changes t's n from 5 to 8, get carries 8 to the terminal. When a scope's closing brace is reached, the destructor runs by itself and the box fades: u first (bye 1), then t (bye 8). The figure draws each object as one box holding its n. Member functions are not stored in the object: their code exists once for the whole class. On common compilers sizeof(Tally) equals sizeof(int), usually 4 bytes; real addresses differ from run to run.

The blueprint

  1. A class describes a new type. Tally says what every Tally object holds, one int called n, and which functions work on it. Writing a class makes no object yet: nothing is in memory.

Using an object

Lifetime

What happens

;; step 1/10 · blueprint
tally.cpp   (one file; it starts with #include <iostream>)
   1  class Tally {
   2    int n;
   3  public:
   4    Tally(int s) : n{s} {}
   5    void add(int k) { n += k; }
   6    int get() const { return n; }
   7    ~Tally() {
   8      std::cout << "bye " << n << '\n';
   9    }
  10  };
a type, like int, but one you designed
no object exists yet: nothing in memory

The two cards are one file, tally.cpp, which starts with #include <iostream>. Build it with g++ -std=c++20 tally.cpp -o tally and run ./tally: it prints 8, bye 1 and bye 8 on three lines. Precisely, an object's lifetime begins when its initialization (here, the constructor) is complete and ends when its destructor call starts. Objects with automatic storage duration, like t and u, are destroyed at the end of the block they were made in, in reverse order of construction.