Module 6 — Self-Assessment¶
Nine questions. Parts A and B are closed-book; Part C wants a compiler. Answers state what a full-credit answer contains, not merely the right word — a question worth two marks that you answered with one word is a question you have not finished.
Part A — Recall and comprehension¶
1. In class Derived : public Base, name three things a Derived object
contains a copy of, and one thing it does not.
(Answer: it contains the base's data members, inherits its public and protected
member functions (reachable subject to access), and shares its static members.
It does not contain a second copy of the base's private data that it may name —
private members are present but inaccessible — and it does not inherit the base's
constructors unless the class writes using Base::Base;. Full credit: the
distinction between "present" and "reachable" appears somewhere in your answer.)
2. Fill the table: for a public, protected and private inheritance, say
what the outside world sees and whether the derived object is-a base.
(Answer: public — the inherited public interface stays public, and yes it is-a. Protected — the inherited public interface becomes protected, visible only to further derived classes; it is not usable as a base by unrelated code. Private — nothing inherited is public or protected; there is no is-a relationship. Full credit: the sentence the mode does not restrict the derived class, it restricts the world, because that is the misconception the table exists to kill.)
3. In what order are the following constructed: the base subobject, the derived class's members, the derived constructor's body — and what is the order on destruction?
(Answer: base, then the derived members in declaration order, then the
constructor body; destruction is the exact reverse. Full credit: mentioning that the
initialiser list's order is irrelevant to the sequence and produces a -Wreorder
warning when it disagrees.)
4. A derived class declares void f(int) where the base has void f(int),
void f(std::string) and void f(). Which of the four are callable on the derived
object, and what is the smallest fix?
(Answer: only f(int). The derived declaration hides every base declaration of
the name. Smallest fix: using Base::f; in the derived class — which restores the
overload set at every call site, rather than qualifying each call. Full credit: saying
that this is hiding, not overriding, and that overriding requires virtual plus an
identical signature.)
5. What is the diamond problem, what does it cost, and what are the two standard responses?
(Answer: two bases inheriting from a common ancestor give the most-derived object
two subobjects of that ancestor, so its state exists twice and an unqualified access
is ambiguous. Cost: silent duplication — no diagnostic — and two answers to one
question. Responses: virtual inheritance to share one subobject, or replace the
relationship with an association when the duplicated state should be shared anyway.
Full credit: noting that virtual inheritance is a poor fit for shared state and is
best reserved for interface-only diamonds.)
Part B — Prediction and explanation¶
6. What does this print, and why?
class Employee {
public:
Employee(int id) : id_(id) { std::cout << "Employee " << id_ << '\n'; }
long long pay() const { return 1000; }
private:
int id_;
};
class Manager : public Employee {
public:
Manager(int id, int reports) : Employee(id), reports_(reports) {
std::cout << "Manager " << reports_ << '\n';
}
long long pay() const { return 2000; }
private:
int reports_;
};
void report(const Employee& e) { std::cout << "pay " << e.pay() << '\n'; }
int main() {
Manager m(7, 3);
report(m);
std::cout << "direct " << m.pay() << '\n';
}
Answer.
The first two lines are the ordering rule: the base is constructed before the derived
body. The last two are hiding: report takes const Employee&, pay is not
virtual, so the call is settled at compile time from the static type and the base's
1000 is printed. m.pay() is written on a Manager object, so ordinary name
lookup finds Manager::pay and prints 2000. Full credit requires all four lines in
order and the word "not virtual" — a student who says "the base version runs
because of slicing" has the symptom without the cause, since nothing was copied here.
7. How many distinct name_ strings does a TeachingAssistant contain, and what
happens to the second one when the first is changed?
class Person { public: std::string name_ = "Ayesha"; };
class Student : public Person {};
class Tutor : public Person {};
class TeachingAssistant : public Student, public Tutor {};
Answer. Two, and the second is untouched:
The object has two Person subobjects, so ta.name_ is ambiguous and the qualified
form names one of them. Full credit: the number, the fact that nothing warns, and one
sentence on what to do — virtual inheritance if the two roles share an identity,
an association if the shared data belongs somewhere else.
8. This compiles. Name the mistake, explain why the compiler is silent, and say what will go wrong at run time.
class BaseItem {
public:
int loanDays() const { return 21; }
};
class Journal : public BaseItem {
public:
int loanDays() const { return 2; }
};
int main() {
std::vector<BaseItem> items;
items.push_back(Journal{});
std::cout << items[0].loanDays();
}
Answer. Two silent mistakes in one program. The push_back slices: a
Journal is copied into a BaseItem object, so the Journal part is gone. And
loanDays is not virtual, so even without the container there would be no dispatch.
The compiler is silent because both operations are legal: converting a derived object
to a base object by copy is allowed, and a non-virtual call is the default. The output
is 21 — no error, no warning, and the wrong loan period. Full credit: naming both
mechanisms and stating that the second is Module 7's subject while the first is a
container-ownership problem that Module 11 solves with std::vector<std::unique_ptr<BaseItem>>.
9. For each relationship, say whether inheritance is defensible, and name the operation that decides your answer.
SavingsAccount/Account(the account haswithdraw,deposit,balance)SavingsAccount/std::vector<Transaction>FixedStack/IntList(as in Example 5)Manager/EmployeeTemperature/double
Answer. 1 — defensible, because every operation remains true (subject to a
withdrawal rule the base must allow the derived class to enforce, which is exactly why
Account::takeOut is protected and refuses). 2 — not defensible: push_back,
insert and clear are meaningless for an account, and the deciding operation is any
of them. 3 — not defensible: insertAt and clear cannot be honoured by a stack. 4 —
defensible: every Employee operation is true of a Manager. 5 — not defensible in
either direction, and the deciding fact is that the built-in types cannot be derived
from at all; conversion or a member is the answer. Full credit: five verdicts, each
with a named operation, and no verdict justified by "it saves code".
Part C — With a compiler¶
10. Write the program that proves the rule in question 3 — one hierarchy of
three levels, a Tracer member in each class, and a predicted order written down
first. Then change one member's declaration order and predict the new trace before
running it.
Answer. This is the lab's requirement, and there is no shortcut: the value is in
the prediction being made before the observation.
examples/module-06/03-construction-and-destruction-order/main.cpp is the reference
implementation, including the -Wreorder error that a wrong initialiser list
produces. A full-credit answer includes the prediction, the real trace, and one
annotated difference if there is one.
Marking yourself¶
| Score | What it means |
|---|---|
| ≥ 80%, every miss explained | Move on to the next module |
| 60–79% | Re-read the sections behind the misses, then retake in a week |
| < 60% | Redo the worked examples and the lab before going further |
A quiz is diagnostic, not a grade. The useful output is the list of concepts that need another look.
Ready to move on when¶
- [ ] You scored ≥ 80%, and any miss has a written explanation, not a shrug
- [ ] Question 6 is answered from the rule, with the word "virtual" in it
- [ ] You can draw the subobject map of a diamond from memory and say how many copies of the shared base it contains
- [ ] You have written a hierarchy, watched its constructor order, and corrected at least one prediction
- [ ] The skills checklist on the module page is ticked with evidence
Next: Summary — the module compressed to what you must retain. · All modules