Module 2 — Exercises¶
Every exercise states its level and its success criterion, so you can tell whether you succeeded without reading a solution. Write your attempt first — reading a solution produces recognition, and recognition is not the skill being tested.
The exercises build on the module's five programs in examples/module-02/. Have them
open.
Warm-up [L1 Understand]¶
W1 — class, object, struct¶
Task. Answer in one sentence each:
- What is the difference between a class and an object?
- What is the only language difference between
classandstruct? - Which of these should be a
structand which aclass, and why? (a)Accountwith a balance that must never go negative; (b)Pointwith an x and a y and no rules; (c)Bookwith an identifier that never changes. - In
examples/module-02/header-source/, which file holds the declarations, and which holds the definitions?
Success looks like. Four answers, none longer than two sentences, and — for question 3 — a reason that mentions a rule rather than a preference.
Stuck? Concepts §1–2.
W2 — predict, then check¶
Task. Predict the exact output, and say which lines fail to compile and why.
#include <iostream>
#include <string>
class Ticket {
public:
Ticket(int number, const std::string& event) : number_(number), event_(event) {}
int number() const { return number_; }
const std::string& event() const { return event_; }
void cancel() { cancelled_ = true; }
bool cancelled() const { return cancelled_; }
private:
int number_;
std::string event_;
bool cancelled_ = false;
};
void show(const Ticket& t) {
std::cout << t.number() << ' ' << t.event() << ' ' << t.cancelled() << '\n';
}
int main() {
const Ticket first(1, "Orientation");
Ticket second(2, "Orientation");
second.cancel();
show(first);
show(second);
// (a)
first.cancel();
// (b)
std::cout << first.number_;
// (c)
std::cout << first.event().size() << '\n';
}
Success looks like. The two printed lines correct, plus a named cause for (a), (b)
and (c): which is a const violation, which is a private-access violation, and which
one is fine even though it looks suspicious. The third one is the point of the
exercise.
Stuck? Concepts §5 and §7.
W3 — which functions must be const?¶
Task. Take the Book class from examples/module-02/01-book-class/main.cpp and
list every member function, marking each one const or not, with a one-line reason.
Success looks like. Five members, correctly marked. If you marked correctPagesTo
as const, re-read §7 — the reason matters more than the mark.
Stuck? Concepts §7.
Core [L2 Apply] · [L3 Modify] · [L4 Analyze]¶
C1 — write a class of your own [L2 Apply]¶
Task. Write a Vehicle class with:
- a registration plate and an odometer reading in kilometres, both set at construction;
drive(int km)— increases the odometer, refuses a non-positive distance;needsService() const— true once the odometer passes 15 000 km;describe() const— prints one line;- no way for a caller to write to the odometer directly.
Then use it: create two vehicles, drive one of them past the service threshold, and print both.
Success looks like. Builds with no warnings under
g++ -std=c++17 -Wall -Wextra; two vehicles with independent odometers; the threshold
crossing at exactly the right kilometre; and a const Vehicle somewhere in your
program that still compiles.
Note. Notice the design decision you are being asked to make: is needsService()
a stored bool or a computed answer? One of the two can go stale. Say which you
chose and why in a comment.
C2 — break it deliberately, four ways [L3 Modify]¶
Task. Take your C1 program and break it in four ways, predicting the message before compiling each one:
- read
vehicle.odometer_frommain; - remove
constfromneedsService()and call it on aconst Vehicle; - delete the semicolon after the class's closing
}; - declare a member function in the class and never define it, then build the program.
For (4) you must separate the declaration from the definition, so split the class into a header and a source file first — which is exercise C4 anyway, and doing them together saves a pass.
Success looks like. Eight lines: four predictions, four actual messages — and each actual matched to its prediction or the difference explained. Every one of the four messages appears, in full, in Misconceptions and mistakes; compare rather than guess.
Stuck? Debugging.
C3 — measure what a change costs [L3 Modify] (deliberately tedious)¶
Task. Add an owner field to:
- Module 1's procedural baseline,
examples/module-01/01-procedural-baseline/main.cpp; - Lab 2's class-based version,
examples/labs/lab-02/solution/— or your own if you have done the lab.
Success looks like. Both programs work and show the owner, plus a table:
| Version | Lines changed | Functions changed | Call sites changed |
|---|---|---|---|
| Procedural | |||
| Class-based |
The table is the exercise. Do not estimate — count, using diff on a copy of each
file. This is the same measurement Module 1 asked for, made again now that you have
seen both shapes, and it is the evidence behind everything the course claims.
C4 — split a class across two files [L2 Apply]¶
Task. Move your Vehicle class into Vehicle.h and Vehicle.cpp, leaving main
in main.cpp, and build it with one command:
Success looks like. It builds and runs from that command, and you can answer:
- What error appears if you omit
Vehicle.cppfrom the command, and at which stage? - Why does
Vehicle.hnot need#include <iostream>even thoughdescribe()prints? - What happens if you remove
#pragma onceand includeVehicle.htwice?
Stuck? Concepts §8.
C5 — who owns the rule? [L4 Analyze]¶
Task. examples/module-02/04-objects-interacting/main.cpp has two rules:
"at most three courses per student" and "at most three students per course". Add a
third: a student may not enrol in the same course twice.
Answer on paper first:
- Which object owns this rule —
Student,Course, or both? - What does each owner need to know to enforce it?
- Where must the check go so the two objects cannot disagree about the outcome?
Then implement it, and prove it with a scripted run that attempts a duplicate enrolment.
Success looks like. One sentence naming the owner and why; the implemented rule; and a demonstration that the duplicate adds nothing to either object.
Full credit includes noticing that if the check lives in Student and the record
is added by Course, a refusal by one must prevent the other's write — the ordering
question from §9 of Concepts.
Challenge [L4 Analyze] · [L5 Design]¶
X1 — design before you code [L5 Design]¶
Task. An organisation records employees. Each has a name, a staff number, and a monthly basic salary. Salaries rise by a percentage after review, and are never reduced. A record must never exist with a salary of zero or less.
Produce, on paper, before writing any code:
- The invariant, in one sentence.
- The public interface, as a table: one row per member, with the question a caller is asking.
- The representation, with a note on why each member is needed and what type it is.
- One thing you deliberately did not provide, and what a caller would do instead.
Then implement it, and build it with no warnings.
Success looks like. A class (not a struct) with no setter for the salary, a
raiseBy-style operation that cannot reduce it, and — critically — a salary stored in
the smallest currency unit or with an explicit comment about why a double was or was
not chosen.
How you will be judged. On the exclusions and the reasons, as much as the code. An interface with nine getters and five setters has not made any decisions.
X2 — find the leaks [L4 Analyze]¶
Task. Read examples/labs/lab-02/solution/library.h and library.cpp. Find every
place where the interface leaks something a caller should not be able to depend on.
At least three are findable:
- something that hands out a mutable pointer to internal data;
- something that makes a type decide how it will be presented;
- something whose name promises less than it does.
For each: what can a caller do with it that the design should have prevented, and what is the smallest change that closes it?
Success looks like. Three specific findings, each citing a line, each with a consequence demonstrated in words — "a caller can now do X, and the catalogue's invariant Y is then false" — and a minimal fix.
Full credit includes proposing a fix that does not require changing any correct
caller. This is a preview of Module 4's central test, and the reference discussion is in
examples/labs/lab-04/solution/justification.md.
X3 — the one-sentence responsibility test [L5 Design]¶
Task. Take the four candidate types from Module 1's analysis — Book, Member,
Loan, Catalogue — and write each responsibility as one sentence without the word
"and".
Then:
- Which of them, if any, fails the test, and what would you split or merge?
Catalogueboth stores items and answers questions about them. Is that one responsibility or two? Argue both ways in two or three sentences, then choose.- Name one thing about the system you deliberately would not make a type, and why.
Success looks like. Four one-sentence responsibilities; for (2) a position taken with a reason, not a summary of the debate; and for (3) a decision that would survive a review.
How you will be judged. This is the analysis step Lab 9 formalises. A design in which everything becomes a type has not made any decisions, and (3) is where you prove you can say no.
Stretch (optional)¶
- S1. In
examples/module-02/04-objects-interacting/main.cpp, makefillWithoutAskingaffect the caller without changing its parameter type fromCourse. Then change it to takeCourse&and say, in two sentences, what you had to give up in the first version and what you gained in the second. Module 9 formalises the vocabulary, but the intuition is available now. - S2. Compile one of your classes with
clang++instead ofg++. Compare the two compilers' messages for the same error from C2, and note which one you found easier to act on — you will be choosing a compiler for years. - S3. Add a
describe()member toCoursein the same example and haveStudent::describe()not call it. Then write a version whereStudentdoes call it, and say which is better and why. (Hint: the answer depends on what a student record is for. Neither answer is wrong; having a reason is what is being marked.)
Solutions¶
Reference programs live in examples/module-02/:
| Exercise | Reference |
|---|---|
| W2 | examples/module-02/02-const-members/main.cpp — the same questions, with two of the three errors commented in place |
| C1, C4 | examples/module-02/05-bank-account/main.cpp, and examples/module-02/header-source/ for the file split |
| C3 | examples/labs/lab-02/solution/ for the class-based version |
| C5 | examples/module-02/04-objects-interacting/main.cpp for the ownership pattern |
| X2 | examples/labs/lab-04/solution/justification.md — the reference discussion, written for a later lab but about exactly these leaks |
C2 has no published solution on purpose: the four messages are in 03-misconceptions-and-mistakes.md, and matching your prediction against them is the exercise.
X1 and X3 have no single right answer. Compare your reasoning with Module 4's
invariant-first order and with examples/labs/lab-04/solution/justification.md.
Next: Lab 2 — the same library desk as Lab 1, rebuilt with types. · All modules