Advanced Interview Bank
Learning Objectives
Answer questions on object layout, dispatch internals, type erasure, variance, CRTP, virtual inheritance, exception safety, architecture, DDD, concurrency and reflection.
Prerequisites: Stage 4.
Intuition and Definition
Advanced questions probe *why* mechanisms work and how designs behave at scale: memory, threads, binaries, distributed systems.
Why It Matters
Senior and staff roles expect you to reason about consequences beyond the code in front of you — ABI, performance, failure modes, team boundaries.
Syntax and Language Notes
Draw diagrams (memory layout, sequence, context maps) while answering; it shows structure and catches mistakes.
Complete Example
Type erasure built by hand — a classic advanced question ("implement a minimal std::function/any_shape") — giving value semantics *and* run-time polymorphism without a common base class:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class AnyDrawable { // value type that can hold ANY type with draw()
struct Concept { // internal interface
virtual ~Concept() = default;
virtual std::string draw() const = 0;
virtual std::unique_ptr<Concept> clone() const = 0;
};
template <class T>
struct Model : Concept { // wraps a concrete type
T obj;
explicit Model(T o) : obj(std::move(o)) {}
std::string draw() const override { return obj.draw(); }
std::unique_ptr<Concept> clone() const override { return std::make_unique<Model>(*this); }
};
std::unique_ptr<Concept> self_;
public:
template <class T>
AnyDrawable(T obj) : self_(std::make_unique<Model<T>>(std::move(obj))) {}
AnyDrawable(const AnyDrawable& o) : self_(o.self_->clone()) {} // value semantics: deep copy
AnyDrawable& operator=(AnyDrawable o) noexcept { self_ = std::move(o.self_); return *this; }
AnyDrawable(AnyDrawable&&) noexcept = default;
std::string draw() const { return self_->draw(); }
}
…gauge 88 | icon oil | gauge 3 |
icon battery | icon oil | gauge 3 |Step-by-Step Walkthrough
Concept is a private interface; Model<T> adapts any T with a draw() to it.
The templated constructor erases the concrete type; afterwards only Concept is visible.
Copying clones the model, so AnyDrawable behaves like a value (no slicing, no shared state).
Gauge and Icon share no base class — the polymorphism is non-intrusive. This is how std::function and std::any work.
Common Mistakes
Describing vtables as guaranteed by the standard.
Claiming exception safety without naming which guarantee.
Microservice answers that ignore partial failure and data ownership.
Concurrency answers that stop at "add a mutex".
Trade-offs and Alternatives
Type erasure costs an allocation and indirect call per object; small-buffer optimization avoids the allocation for small types (as std::function does).
Real-World Use
std::function, std::any, Boost.TypeErasure, Sean Parent's "runtime polymorphism" talks, GUI widget containers, plugin interfaces.
Practice
Q1. How are vtables and vptrs typically implemented?
Answer: One vtable per polymorphic class with function pointers (plus RTTI/offset data); one vptr per object (per polymorphic base subobject), set by constructors; virtual calls load the vptr, index the table, call indirectly (Module 55).
Q2. What is part of a C++ class's ABI?
Answer: Size, alignment, member offsets, vtable layout and order, mangled names, calling conventions, inline function bodies — changing any breaks binary compatibility (Modules 55, 63).
Q3. Explain type erasure.
Answer: Hide a concrete type behind a uniform interface by wrapping it in an internal model implementing a private concept interface; users get value semantics and run-time polymorphism without inheritance (example above).
Q4. Covariance vs contravariance?
Answer: Covariant: Producer<Derived> usable as Producer<Base> (outputs). Contravariant: Consumer<Base> usable as Consumer<Derived> (inputs). C# out/in, Java ? extends/? super; C++ templates are invariant (Module 17).
Q5. CRTP and policy-based design?
Answer: Static polymorphism via Derived : Base<Derived> and configuration via template policy parameters — zero overhead, compile-time choice (Module 58).
Q6. Virtual inheritance cost?
Answer: Extra indirection to locate the shared base subobject, larger objects, the most-derived class constructs the virtual base (Modules 7, 56).
Q7. How do you achieve the strong exception guarantee?
Answer: Build changes on copies, commit with no-throw operations (swap/move); copy-and-swap (Modules 12, 14).
Q8. DIP vs DI?
Answer: Inversion is about dependency direction (details depend on abstractions owned by policy); injection is a technique for supplying dependencies (Modules 19, 22).
Q9. Hexagonal vs clean architecture?
Answer: Both keep the core independent via inward dependencies; hexagonal speaks of ports/adapters, clean names concentric rings and input/output boundaries (Modules 72–73).
Q10. What is an aggregate, and how big should it be?
Answer: A consistency boundary changed through its root; as small as the invariants allow — usually one transaction per aggregate (Module 74).
Q11. What is a bounded context?
Answer: A boundary within which a model and language are consistent; natural service/team boundaries (Module 74).
Q12. How do domain events relate to integration events?
Answer: Domain events are in-process facts within a context; integration events are published, versioned contracts for other services, ideally via an outbox (Module 76).
Q13. How do you make a class thread-safe?
Answer: Prefer immutability or confinement; otherwise protect all shared state with one lock per invariant, make compound operations atomic, never call out while holding locks, document the guarantee (Module 60).
Q14. What is reentrancy and why does it matter for observers?
Answer: A callback re-entering an object while it's mid-operation; observers may modify subscriptions or trigger new notifications — iterate over snapshots and avoid holding locks during callbacks (Modules 41, 60).
Q15. Risks of reflection?
Answer: Performance, broken encapsulation, trimming/AOT issues, and security (instantiating arbitrary types, accessing private data) (Module 57).
Q16. How would you refactor a god class?
Answer: Characterization tests, map responsibilities, extract cohesive classes one at a time with delegation, keep behaviour identical, repeat by change frequency (Modules 52–53).
Q17. Design an extensible device-driver architecture.
Answer: Small driver interfaces per capability (read, write, configure), a HAL port owned by the application, adapters per hardware/simulator, a registry/factory for discovery, versioned driver APIs, and contract tests run against every driver (Modules 29, 34, 63, 72).
Interview Question
Q1. "Design an extensible payment system that supports new providers, retries and auditing."
Answer: Core: Payment aggregate with states (Pending, Authorized, Captured, Failed, Refunded) and invariants; PaymentProvider port (authorize, capture, refund) implemented by adapters per provider; idempotency keys per operation; an outbox for PaymentCaptured events; retries only for transient, idempotent calls with back-off and a circuit breaker per provider; reconciliation jobs for unknown outcomes (timeouts); an append-only audit log; provider selection via strategy/configuration; contract tests for every adapter (Modules 43, 51, 69, 72, 76).
Quick Quiz
Q1. Which standard types use type erasure?
Answer: std::function, std::any (and std::shared_ptr's deleter).
Q2. One transaction should usually modify how many aggregates?
Answer: One.
Q3. What breaks when you add a virtual function to a published class?
Answer: The vtable layout (ABI).
Summary
Explain mechanisms, consequences and failure modes; sketch diagrams.
Related: Stage 4.