Skip to content

Quiz 7 — Answers

Back to the quiz


Q1 — B, the dynamic type of the object. For a virtual call through a pointer or reference, the function is chosen at run time from the type of the object actually there — not from the declared type of the pointer. That is what lets one loop serve many derived types.

Q2 — False. override changes nothing at run time. It is a compile-time check: it tells the compiler "I intend to override a virtual function", so a mismatch (a typo in the signature, a missing const) becomes a compile error instead of a silently new function.

Q3 — A, B, D. A virtual destructor makes deleting through a base pointer destroy the whole object (A); without it the behaviour is undefined (B); and it enables the derived destructor to run (D). It does not make the class abstract (C) — only a pure virtual function does that.

Q4 —

base
derived
derived

greet() is not virtual, so ref.greet() uses the static type Base — printing "base". hello() is virtual, so it uses the object's real type Derived — "derived". d.greet() knows the type is Derived, so it prints "derived".

Q5 — It prints ..., because the Dog was sliced. std::vector<Animal> stores Animal values, so push_back(Dog{}) copies only the Animal part; the override is lost. The fix is to store pointers — std::vector<std::unique_ptr<Animal>>.

Q6 — Base has no virtual destructor, so delete p; (a pointer whose static type is Base*) has undefined behaviour: in practice only ~Base runs, so anything Derived owns leaks. Fix:

class Base {
public:
    virtual ~Base() { }
};

The rule: a class designed to be deleted through a base pointer needs a virtual destructor.

Q7 — Base::describe runs. During base-class construction the object's dynamic type is Base — the Derived part does not exist yet — so the virtual call cannot reach the override. The general rule: a virtual call in a constructor or destructor never dispatches to a more-derived override.

Q8 — Because a reference or pointer can refer to a derived object while keeping its real type, so the compiler can look up the actual type at run time. A value parameter or container copies the object into a base object, cutting off the derived part (slicing), so there is nothing left to dispatch to.

Q9 — std::vector<std::unique_ptr<Shape>>, where Shape is an abstract base with a pure virtual area(). The loop uses only the Shape interface:

for (const auto& s : shapes) std::cout << s->area() << '\n';

and never names a concrete shape — the call dispatches to the real type at run time.

Q10 — virtual makes the call resolve at run time to the most-derived override — it changes behaviour. override is a compile-time promise that this function does override a virtual one — it changes what the compiler checks, not what the program does. A class can have virtual without any override, and override without virtual on the same line is an error.


Marking. One point per question (Q3 all-or-nothing). Then read Marking Yourself.