Skip to content

Module 1 — Misconceptions and Mistakes

Two different problems, fixed two different ways.

A misconception is a wrong mental model. Information does not remove it: only seeing the case where it gives the wrong answer does. A mistake is a concrete error with a concrete fix, and information is exactly what removes it.


Part 1 — Misconceptions

"Object-oriented just means using classes."

Why this feels true. Every example of object-oriented code you have seen contains the keyword class. The two seem to be the same thing.

Where it breaks. Here are two programs that both compute a total. The second uses classes, and is worse.

// 1. A procedure. Ten lines, obvious, nothing hidden.
double total(const std::vector<double>& prices) {
    double sum = 0.0;
    for (double price : prices) sum += price;
    return sum;
}

// 2. The same work, object-oriented style. Guess which one a reader prefers.
class TotalCalculator {
public:
    explicit TotalCalculator(std::vector<double> prices) : prices_(std::move(prices)) {}
    void calculate() { total_ = 0.0; for (double p : prices_) total_ += p; }
    double result() const { return total_; }
private:
    std::vector<double> prices_;
    double total_ = 0.0;
};
// ... and the caller must now remember to call calculate() before result().

The correct model. Objects earn their place when there is state with rules about it that must survive being passed around. Prices in a list have no rules; a sum has no state. A bank account has both.

The re-test. In Module 4, look at Book::describe() in the lab's reference solution and decide whether that member belongs on the type. It is a real question, and the justification table in examples/labs/lab-04/solution/justification.md gives the reference answer.


"A class is a struct with functions in it."

Why this feels true. Structurally, that is almost exactly what the syntax says.

Where it breaks. Take the Catalogue from Example 2 and make every member public. It still compiles, still runs, and now catalogue.count_ = 99; corrupts it from anywhere in the program. The functions were never the point.

class Catalogue {
public:                     // <- change this one line
    int count_;
    int ids_[8];
    bool onLoan_[8];
};

The correct model. The functions are not the feature; the boundary is. A class says "here is what may be done with this, and here is everything else".

The re-test. Module 4's first worked example is a class with private data whose invariant is broken anyway, through its own public interface. That example is the full answer to this misconception, and it is worth remembering that private alone did not save it.


"OOP is always better than procedural programming."

Why this feels true. You are reading a course called Object-Oriented Programming, and the phrase is used as a compliment.

Where it breaks. Consider a 40-line script that reads a CSV file and prints column totals. Every version with classes is longer, and none is clearer. Or consider std::sort — a free function, not a method on a collection class.

The correct model. Decomposition is a tool, chosen for the problem. The procedural baseline in Lab 1 is not a straw man to be laughed at: it is a correct program whose cost appears only when the requirements change.

The re-test. In Lab 1's reflection you must name what hurt. In Lab 2 you must report what improved. If nothing improved, you have found a case where the answer is "this program did not need objects" — and saying so, with evidence, is a higher grade than pretending otherwise.


"Compiling is one step."

Why this feels true. The command is one line and produces one file.

Where it breaks. Example 3(b): a program with a declared-but-undefined function compiles perfectly and then fails to produce an executable. Two stages, two failures.

g++ -std=c++17 -Wall -Wextra -c main.cpp    # succeeds — this is one stage
g++ -std=c++17 -Wall -Wextra main.cpp -o main  # fails at the NEXT stage

The correct model. Preprocessing → compiling → linking are separate, with separate error messages, fixed in separate places.

The re-test. 04-debugging.md gives you a three-question diagnosis for link errors. Use it the first time you meet one in your own code.


"using namespace std; is normal — everybody does it."

Why this feels true. Almost every tutorial has it on line 3.

Where it breaks. Section 3 of 01-concepts.md shows a count of your own colliding with std::count the moment <algorithm> is included. The message you get is reference to 'count' is ambiguous, which says nothing about the cause.

#include <iostream>
#include <algorithm>
using namespace std;      // add this line...
int count = 0;            // ...and this stops compiling

The correct model. It is a convenience that is safe in a 30-line example and unsafe in a real program — and actively harmful in a header, where it leaks into every file that includes it.

The re-test. Type std:: for the rest of the course. When you meet a library that has a name you want (std::move, std::size), write using std::move; inside the function that needs it.


"Private data means encapsulation."

Why this feels true. The two are taught together, and private is the keyword you are told to use.

Where it breaks. This is Module 4's whole subject, so it is only flagged here. The short version: a private member with a public setter is as breakable as a public member, and looks safer, which is worse.

The correct model. Encapsulation is the property that an object's rule cannot be broken from outside — not the property that its fields are hard to name.

The re-test. By the end of Module 4, you should be able to write two lines from main that break a class you wrote, and then change the class so those lines cannot be written again. That is Lab 4.


Part 2 — Common programming mistakes

Symptom Cause Fix
error: expected ';' before '}' token A missing semicolon, usually on the line above the report Look one line earlier than the number says
error: 'cout' was not declared in this scope #include <iostream> missing, or std:: missing Add the include; write std::cout
error: 'string' was not declared in this scope #include <string> missing — <iostream> does not provide it Include <string>
fatal error: iostream: No such file or directory Compiled with gcc instead of g++ Use g++
fatal error: iostream: No such file or directory (again, with g++) The toolchain is not installed or not on PATH Set Up Your Toolchain
undefined reference to 'main' No main function, or the wrong file was named Every program needs exactly one main
unrecognized command line option '-std=c++17' The compiler predates C++17 Install a newer compiler
Builds, then prints nonsense or crashes Undefined behaviour: out-of-bounds access, an uninitialised value, a dangling pointer Not a setup problem. This is what Modules 4, 10 and 11 exist for — log it and carry on

Mistake 1 — the error is on the line above [L3 Modify]

Reproduce it.

int main() {
    int count = 5
    return 0;
}

What you see.

error: expected ';' before 'return'

Why. The compiler reports the point at which the missing token would have had to appear — which is the beginning of the next line, not the end of the previous one.

Fix it. Add the semicolon to the line the compiler did not name.

Prevent it. When the reported line looks correct, read the one above it. This single habit removes most of the time beginners lose to compilation.

Reproduce it.

g++ -std=c++17 -Wall -Wextra main.cpp -o main

where main.cpp calls area() and never defines it.

What you see.

undefined reference to `area(double, double)'
collect2: error: ld returned 1 exit status

Why. The function was declared, so the compiler was satisfied. The linker then looked for the definition and found none.

Fix it. Define the function — or, once a program has more than one file, add the file that defines it to the compile command.

Prevent it. When you see undefined reference, ask three questions, in order: Is it defined anywhere? Is that file in my command? Does the signature match exactly? The third question matters most from Module 2 onwards, where const is part of the signature.

Mistake 3 — using namespace std; plus a name of your own [L3 Modify]

Reproduce it.

#include <iostream>
#include <algorithm>
using namespace std;
int count = 0;
int main() { cout << count << '\n'; }

What you see.

error: reference to 'count' is ambiguous

Why. std::count and your ::count are both visible unqualified.

Fix it. Remove the using line.

Prevent it. Never write it. Type std::.


Next: Debugging — the technique this module adds, and a real diagnosis worked end to end.