Skip to content

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.

Employee 7
Manager 3
pay 1000
direct 2000

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:

ta.Student::name_ = Ayesha Khan      // after ta.Student::name_ = "Ayesha Khan"
ta.Tutor::name_   = Ayesha

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.

  1. SavingsAccount / Account (the account has withdraw, deposit, balance)
  2. SavingsAccount / std::vector<Transaction>
  3. FixedStack / IntList (as in Example 5)
  4. Manager / Employee
  5. Temperature / 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