Exercises — Constructors — default, parameterized, copy, delegating
Level 1 — Recognition
Recall Solution L1·Q1
- (i) Default — no arguments supplied, so
Point()runs. - (ii) Parameterized — two
ints go toPoint(int,int). - (iii) Copy —
cis born as a copy of the already-builtb. This is copy-initialization, but it still calls the copy constructorPoint(const Point&). - (iv) Copy — direct form
Point d(b); same copy constructor as (iii).
The key tell: what is on the right? Nothing → default. Raw values → parameterized. An existing object → copy.
Recall Solution L1·Q2
- (a) Invalid — a constructor has no return type, not even
void. Writingvoidturns it into an ordinary member function namedPoint. - (b) Valid — the default constructor.
- (c) Valid — a parameterized (single-arg) constructor.
- (d) Invalid —
intreturn type; same reason as (a).
Rule to memorise: same name as the class, and nothing where a return type would go.
Level 2 — Application
Recall Solution L2·Q1
Rect(int a, int b) : w(a), h(b), area(a * b) {}Why the list and not the body? In the list each member is direct-initialized once. If you did area = a*b; inside {}, area would first be default-initialized to garbage, then assigned — two steps, and for const members it would be impossible.
Watch the order: area is declared last, so it is initialized last — after w and h exist. Good. Result: w=3, h=4, area=12.
Recall Solution L2·Q2
- (i) Compiles — direct-initialization is always allowed.
- (ii) Rejected —
= 5needs an implicitint → Meterconversion, whichexplicitforbids. - (iii) Compiles —
Meter(5)is an explicit conversion (you named the type), then copy-initialized intoc.
See explicit keyword and implicit conversions for the full story.
Level 3 — Analysis
Recall Solution L3·Q1
Counting copy-constructor calls with elision disabled:
- Line 1: default constructor, 0 copies.
- Line 2: copy-init
bfroma→ 1 copy. - Line 3: passing
aby value builds the parameterxfroma→ 1 copy. - Line 4:
make()buildslocal, returns it by value → 1 copy (return), thencis copy-initialized from that returned temporary → 1 copy. That is 2 copies.
Total = 4 copies. (In real C++17 compilers, mandatory copy-elision would erase the line-4 copies, giving 2 — but the question fixed "no elision" to test understanding of when the copy constructor is eligible to fire.) See Move Semantics — move constructor for how moves replace many of these.
Recall Solution L3·Q2
The compiler-supplied copy constructor does a member-wise (shallow) copy: it copies the pointer value data, so x.data == y.data — both point at the same heap array.
Nothing bad happens yet. The disaster is deferred to destruction: when y dies its destructor runs delete[] data; when x dies its destructor runs delete[] on the same already-freed pointer → double free, undefined behaviour / crash.
Fix: write a deep-copy constructor (data(new int[o.n]) then copy elements), plus copy assignment and destructor — the Rule of Three Five Zero.
Level 4 — Synthesis
Recall Solution L4·Q1
class Circle {
int x, y, r;
public:
Circle(int x, int y, int r) : x(x), y(y), r(r) {} // target
Circle(int r) : Circle(0, 0, r) {} // delegates
Circle() : Circle(0, 0, 1) {} // delegates
};Why this shape? The target holds the only member-initialisation. Delegates carry just the delegating call in their list — you may not also list members there. The target runs fully first, then the delegate's (empty) body runs. Circle c; ends with x=0, y=0, r=1; Circle d(5); ends with x=0, y=0, r=5. See Member Initializer Lists.
Recall Solution L4·Q2
Buf(const Buf& o) : data(new int[o.n]), n(o.n) {
for (int i = 0; i < n; ++i) data[i] = o.data[i];
}data(new int[o.n])— allocate a separate array of the same length.- the loop copies each element so contents match.
After this,
y.data == x.datais false (distinct addresses), while the contents are equal. Now each object owns its own memory, so twodelete[]s free two different arrays — no double free.
Level 5 — Mastery
Recall Solution L5·Q1
Constructing box: members build in declaration order (a then b), not list order:
a('A')→ printsCAb('B')→ printsCBSo far:CACB.
M solo('S') → parameterized → CS. Now: CACBCS.
M dup = solo → copy constructor → Kdup uses solo.id = 'S' → prints KS. Now: CACBCSKS.
Destruction at end of main — reverse order of construction. Objects were completed in the order box, solo, dup; local destruction is LIFO, so: dup first, then solo, then box. And inside box, members destruct in reverse declaration order (b then a):
dup→DSsolo→DSbox:b→DB, thena→DA
Destruction tail: DSDSDBDA.
Full output: CACBCSKSDSDSDBDA
Recall Solution L5·Q2
A() delegates to A(int), which delegates back to A() — a delegation cycle A() → A(int) → A() → …. No constructor ever actually initialises v; this is undefined behaviour / non-terminating construction.
Minimal fix: make one constructor a real target that initialises members directly, and let the other delegate to it:
struct A {
int v;
A(int x) : v(x) {} // TARGET — sets v, no delegation
A() : A(0) {} // delegates to the target
};Now delegation forms a tree ending at a genuine initialiser. A a; gives v=0; A b(7); gives v=7.
Figures


Recall One-line self-test
Predict the L5·Q1 output without peeking ::: CACBCSKSDSDSDBDA