Module 1 — Concepts and Explanations¶
Read this with a compiler open. Every section ends with code, and the code is the point — the paragraphs exist to tell you what to look for in it.
1. What actually happens when I press compile?¶
Four stages, and the course's one command runs them all:
| What you wrote | What the compiler produces | What it needs from you |
|---|---|---|
A .cpp file — a translation unit |
An object file (.o) |
Correct C++ |
| Several object files | One executable | Every name to have exactly one definition |
- Preprocessing.
#include <iostream>pastes the contents of that header into your file.#defineis expanded. The result is a single, large source file. - Compilation. That source is translated into machine code for one file, producing an object file. Errors here are syntax, type and name errors.
- Linking. The object files, plus the standard library, are combined and every referenced name is matched to a definition. Errors here are missing things errors — "you used this, but nobody ever defined it".
- Running. Now, and only now, does behaviour matter.
You can stop after stage 2 to inspect what the compiler thinks:
Why this matters before you write a single class: the two kinds of failure you will meet all course long come from different stages, look different, and are fixed in different places. Section 2 makes the difference concrete.
2. Why does my program fail twice, in two different ways?¶
Three failures, three messages, three fixes. Learn to tell them apart now, because every module after this assumes you can.
| Failure | When | A typical message | Where the mistake is |
|---|---|---|---|
| Compile error | Stage 2 | error: expected ';' before '}' token |
In the code the compiler is reading |
| Link error | Stage 3 | undefined reference to 'area(double, double)' |
In a missing definition, or a file not named in the command |
| Runtime error | Stage 4 | Anything from wrong output to a crash | In your logic or your memory use |
The link error is the one beginners meet last and understand least, because the code looks finished. Here it is deliberately:
// broken.cpp — this compiles, and then fails to link
#include <iostream>
double area(double width, double height); // declared (a promise)
int main() {
std::cout << area(3.0, 4.0) << '\n'; // used
}
// ... and never defined. The promise was not kept.
/tmp/cc7x2LqF.o: in function `main':
broken.cpp:(.text+0x1e): undefined reference to `area(double, double)'
collect2: error: ld returned 1 exit status
The odd name at the start, cc7x2LqF.o, is the compiler's own temporary object file: its name
is random and will differ on your machine, and nothing in your source produces it. The message
names main as the location — but main is not wrong. It is the
caller. The mistake is an absent definition. 04-debugging.md turns this into a
routine thirty-second diagnosis.
3. Why does std:: keep appearing in front of everything?¶
The standard library has thousands of names: cout, string, vector, sort,
min, max, count, distance, swap. Your program will invent its own names
too. Without somewhere to file them, collisions are guaranteed.
A namespace is that filing system:
#include <iostream>
namespace library {
int count = 3; // library::count
}
int count = 7; // the global ::count
int main() {
std::cout << "mine: " << count << '\n'; // 7
std::cout << "theirs: " << library::count << '\n'; // 3
}
std::cout says "the cout that lives in std". Everything in the standard
library lives in std, which is why std:: is everywhere.
Why the course avoids using namespace std;. In a small file it is harmless,
and in a large program it is a trap:
#include <iostream>
#include <algorithm> // provides std::count
// using namespace std; <-- uncomment this one line
int count = 0; // and now this becomes ambiguous
int main() {
std::cout << count << '\n';
}
In a header, the damage is worse: everything that includes your header inherits the
collision. So the rule is: type std::, or be explicit about which names you
actually want (using std::cout;) — and never using namespace std; in a header.
You will see using namespace std; in other tutorials
It is not a lie, and it is not good practice. It is a shortcut that works in
30-line teaching examples and causes real bugs in 3,000-line programs. The
course uses std:: everywhere so that your eye learns to read it.
4. Why does my program get harder to change as it grows?¶
This is the question the whole course answers, so here it is as a concrete failure rather than a claim.
A small library keeps four items. Three parallel arrays, four functions:
// the shape of it — the full program is examples/module-01/01-procedural-baseline/main.cpp
std::string g_titles[4] = {"C++ Primer", "Effective Modern C++",
"Clean Code", "UML Distilled"};
int g_ids[4] = {1001, 1002, 1003, 1004};
bool g_onLoan[4] = {false, true, false, true};
It works. So where is the fragility? In four specific places:
1. The index is the only link between the arrays. g_titles[2] and
g_ids[2] describe the same item because you keep them aligned. Nothing checks
it. A function that sorts one array and forgets the others silently corrupts every
record.
2. Every operation has to be handed the data it needs.
bool findById(int id, int& foundIndex); // the answer comes out of a parameter
bool lend(int id, const std::string& by); // returns false for four different reasons
lend returns false when the identifier is unknown and when the item is
already out. The caller cannot tell the two apart without asking again.
3. The rules live in the writer's head. "An item is on loan or available, never
both." Where is that written down? In the reader's memory of the code. lend must
remember to check it; returnItem must remember to check it; the printer must
remember not to print both.
4. Everything is reachable. g_onLoan[2] = false; compiles from anywhere in the
file, and nothing connects it to the loans that were recorded elsewhere.
Now extend it. Add a publicationYear field. Count the places you must touch:
| Place | Why |
|---|---|
| the array declaration | a fourth array |
addItem |
a new parameter, and a new assignment |
| the menu handler | to read the new value from input |
printItem |
to show it |
| the search that returns by index | because the caller now assumes it exists |
| every test and every caller | because the parameter list changed |
Six places, for one field. That number is the argument for everything that follows.
5. What would it look like if the data carried its own behaviour?¶
The same catalogue, with the data and the rules in one place — the second of the
two Module 1 programs, examples/module-01/03-first-class/main.cpp:
class Catalogue {
public:
void addItem(int id, const std::string& title) {
ids_[count_] = id;
titles_[count_] = title;
onLoan_[count_] = false;
++count_;
}
bool lendItem(int id) {
const int index = indexOf(id);
if (index < 0 || onLoan_[index]) {
return false;
}
onLoan_[index] = true;
return true;
}
// ...
private:
int count_ = 0;
int ids_[kMaxItems] = {0};
std::string titles_[kMaxItems];
bool onLoan_[kMaxItems] = {false};
};
Three differences, and they are the whole course in miniature:
- The data is one thing now, not four things that must stay aligned.
addItemupdates all of it in one place, so it cannot get out of step. - The rules have a home. "Available or on loan, never both" is enforced inside the type, by the only operations that can change it.
- Callers speak the system's language.
catalogue.lendItem(1001)instead oflend(1001, titles, ids, onLoan, count).
Does the change get cheap? Adding publicationYear now touches: the private data,
addItem, and the printer. And every caller is unaffected, because the parameter
list did not change.
6. What does "object-oriented" actually promise?¶
Four ideas, each of which solves one of the problems in section 4. This is a preview — the module named beside each one is where it becomes real work.
| Pillar | The problem it solves | Where it is taught |
|---|---|---|
| Abstraction | Callers should ask for what the system means, not how it is stored | Module 4 |
| Encapsulation | A rule must be impossible to break from outside, not merely discouraged | Module 4 |
| Inheritance | Two kinds of thing genuinely share a description, and duplication would drift | Module 6 |
| Polymorphism | One loop must handle every kind of thing without asking which kind it is | Module 7 |
examples/module-01/04-pillars-preview/main.cpp shows all four in about eighty
lines. Do not try to master it — read it as a map of where you are going:
std::vector<const LendableItem*> items{&book, &dvd};
for (const LendableItem* item : items) {
std::cout << " " << item->describe() << '\n'; // one loop, two kinds
}
A caution about "object-oriented". It does not mean "use classes". A 30-line script that converts a file format needs no classes at all, and adding them makes it longer without making it better. Objects earn their place when there is state with rules about it that must survive being passed around. That is the test this course keeps applying.
7. Where do the classes come from?¶
Not from the syntax. From the problem description.
| In the description | Becomes |
|---|---|
| A noun that has data attached — book, member, loan | A type |
| A verb the system performs — borrow, return, search | An operation on a type |
| A rule — "a member may hold at most three items" | An invariant, enforced by an operation |
| A noun that is only a value — a due date, an identifier | A simple type, or a plain value |
Applied to the campus library system this course builds across sixteen labs:
| Candidate | Responsibility, in one sentence | Type or operation? |
|---|---|---|
Book |
An item that can be lent, with a title and an identifier | type |
Member |
A borrower with a name and a current holding | type |
Loan |
The record that a particular member has a particular item | type |
Catalogue |
Holds the items and members; answers questions about them | type |
| lending an item | The system's verb: check, mark, record | operation on Catalogue |
| "at most three items" | The rule that must hold for every member | invariant |
Two checks before you commit. First: can you state the type's responsibility in one sentence without "and"? If not, it is probably two types. Second: name one thing you deliberately did not make a type. A design where everything becomes a class is not a design.
This analysis is Lab 1's last step
Do it on paper before you write any code. In Lab 2 you will turn these candidates into real types, and a vague analysis now costs an hour then.
8. What do I already need to be able to read?¶
Three things, at reading level. Module 10 gives them the depth they deserve.
#include <iostream>
int main() {
int value = 5;
int* pointer = &value; // `pointer` holds the ADDRESS of `value`
int& alias = value; // `alias` IS another name for `value`
*pointer = 7; // write through the pointer
std::cout << value << '\n'; // 7 — same object
alias = 9; // write through the reference
std::cout << value << '\n'; // 9 — same object
int copy = value; // a separate object
copy = 0;
std::cout << value << '\n'; // 9 — the copy changed, not `value`
}
| Idea | Written | Means | Watch for |
|---|---|---|---|
| Value | int n |
Its own object | Changes here do not affect the caller |
| Reference | int& r |
Another name for an existing object | Must be bound at creation; cannot be null |
| Pointer | int* p |
An address, which may be nullptr |
Dereferencing nullptr or a stale address is undefined behaviour |
A by-value parameter is a copy, which is why examples/module-01/02-values-references-pointers/main.cpp
prints "unchanged" for the first of its three functions and "changed" for the other
two. Predict that output before you run it — it is the cheapest understanding test
in the module.
Undefined behaviour, in one sentence: the program may do anything, including appear to work. "It ran on my machine" is never evidence that code is correct. The course returns to this in Modules 4, 10 and 11.
Checkpoint¶
Answer these before moving to the worked examples. If any answer is not immediate, re-read the section named beside it.
- Name the four stages between saving a
.cppfile and seeing output. Which stage catches a missing semicolon, and which catches a missing function definition? (Section 1 and 2.) - Predict: what does this print, and why? (Section 8: the parameter is a copy.)
- A program compiles cleanly and then prints nonsense. Which of the three failure
kinds is that, and what is the most likely cause? (Section 2;
04-debugging.mdhas the list.) - Name one concrete advantage of
Catalogueover the parallel arrays, and one thing that the class version still does not protect. (Sections 4 and 5 — the second half is the honest part, and Module 4 answers it.) - Your friend says "object-oriented means using classes". Give one example where a class would make a program worse. (Section 6.)
Next: Worked examples — four annotated programs, each with a prediction prompt. · All modules