Exercises — Classes — member functions, access specifiers (public, private, protected)
Every solution assumes the compiler is your bouncer: if a line breaks an access wall, it is rejected at compile time — it never runs. Look at the wall diagram below whenever you need to remember who may touch what.

Level 1 — Recognition
(Can you read the rules straight off the page?)
L1.1
State the access level (public / private / protected) of each member below, given no keyword is written before it.
struct Point { double x; void move(); };
class Box { double w; void grow(); };Recall Solution
The rule: struct defaults to public, class defaults to private. Nothing here changes that default, so:
Point::x→ public ·Point::move→ publicBox::w→ private ·Box::grow→ private
What it looks like: on the wall diagram, Point's members sit in the open blue zone; Box's members sit behind the red private wall.
L1.2
For each access specifier, tick which of {same class, derived class, outside main} can reach it.
Recall Solution
Straight from the table in the parent note:
| Specifier | Same class | Derived | Outside |
|---|---|---|---|
public |
✅ | ✅ | ✅ |
protected |
✅ | ✅ | ❌ |
private |
✅ | ❌ | ❌ |
Mnemonic: Public Pals, Protected Progeny, Private Personal.
Level 2 — Application
(Apply the rules to concrete code.)
L2.1
Which lines compile, which fail, and why?
class Account {
private:
double balance = 0;
public:
void deposit(double a) { balance += a; }
double get() const { return balance; }
};
int main() {
Account a;
a.deposit(50); // (1)
a.balance += 10; // (2)
double b = a.get(); // (3)
a.balance = a.get(); // (4)
}Recall Solution
- (1) compiles —
depositis public, called from outside. ✅ - (2) FAILS —
balanceis private;mainis outside the class. ❌ - (3) compiles —
getis public. ✅ - (4) FAILS — the left side
a.balancetouches a private member from outside. ❌ (The right sidea.get()is fine, but ONE illegal token kills the line.)
After the two legal lines run, the balance would logically be . There is no runtime output here — the point is which lines the bouncer lets past the door.
L2.2
Complete withdraw so it never lets the balance go negative. Then compute the balance after this sequence: deposit(100); withdraw(30); withdraw(200); deposit(5);.
void withdraw(double a) {
// fill in: reject if it would overdraw
}Recall Solution
The only door into the data must also be the guard:
void withdraw(double a) {
if (a > balance) return; // reject overdraft, do nothing
balance -= a;
}Trace it starting from :
deposit(100)→ balancewithdraw(30)→ , allowed → balancewithdraw(200)→ , rejected → balance staysdeposit(5)→ balance
Final balance . The rejected withdrawal proves the guard works: an invalid state (negative balance) is made unrepresentable.
Level 3 — Analysis
(Reason about corner cases and mechanisms.)
L3.1 — The per-class, not per-object, wall
Does this compile? Explain precisely why.
class Account {
private:
double balance;
public:
bool richerThan(const Account& other) const {
return balance > other.balance; // (*)
}
};Recall Solution
Yes, it compiles. Line (*) reaches into other.balance, which is private — yet this is inside a member function of Account. The privacy wall is per-class, not per-object: any Account method may read the private members of any other Account.
Think of it as: the wall separates Account code from non-Account code, not one cookie from another cookie. Related tool: friend functions and classes extends this trust to chosen outsiders.
L3.2 — Why const blocks a call
Given double area() const { return w * h; } inside Rectangle, why does removing the const break this line, and what is the fix?
const Rectangle r = makeUnitSquare();
double s = r.area(); // want this to compileRecall Solution
r is a const object — a promise nothing about it will change. When you call r.area(), the compiler must be sure area won't modify r. The only proof it accepts is the const keyword after the signature. Without it, the compiler cannot prove safety, so it refuses the call even though area genuinely changes nothing.
Fix: mark every read-only member const. For a unit square (), area() returns . See const correctness and this pointer (a const method receives a const this).
Level 4 — Synthesis
(Build a whole correct class from a spec.)
L4.1 — Temperature class
Write a class Temperature that:
- stores Celsius internally, private;
- has
setC(double)that clamps any value below absolute zero () up to exactly ; - has
getC() constandgetF() const, where .
Then compute getF() after setC(-300); setC(25); and after setC(-300); alone.
Recall Solution
class Temperature {
private:
double c = 0;
public:
void setC(double v) {
if (v < -273.15) v = -273.15; // clamp at absolute zero
c = v;
}
double getC() const { return c; }
double getF() const { return c * 9.0 / 5.0 + 32; }
};Trace A — setC(-300) clamps to , then setC(25) overwrites to :
Trace B — setC(-300) alone clamps to :
(This is absolute zero in Fahrenheit — a nice sanity check.) The clamp lives inside the only door to c, so no caller can ever store an impossible temperature.
Level 5 — Mastery
(Subtle interactions that separate fluent from shaky.)
L5.1 — protected across an inheritance chain
Predict compile/fail for each marked line.
class Shape {
protected:
double area = 0;
private:
int id = 7;
};
class Circle : public Shape {
public:
void set() {
area = 3.14; // (1)
id = 9; // (2)
}
};
int main() {
Circle c;
c.set();
double a = c.area; // (3)
}Recall Solution
- (1) compiles —
areaisprotected;Circleis a derived class → family access granted. ✅ - (2) FAILS —
idisprivateinShape.privateis NOT inherited-accessible; even children can't touch it. ❌ - (3) FAILS —
mainis outside;protectedis invisible to the outside world just likeprivate. ❌
The single distinction between private and protected is exactly line (1) vs. what would happen if area were private. See Inheritance in C++.
L5.2 — the aggregate final answer
Assuming lines (2) and (3) above were deleted so the file compiles, what value does c.area hold after c.set()? And what is id after construction?
Recall Solution
c.area(set by the derived method through the protected wall).c.id(its in-class initialiser; the derived class never touched it, and never could).
So the object is — a clean demonstration that protected opens the family door while private keeps a member sealed even from its own children.
Connections
- Encapsulation & Data Hiding
- Constructors and Destructors
- Inheritance in C++
- const correctness
- friend functions and classes
- this pointer
- struct vs class