0% found this document useful (0 votes)
3 views19 pages

4 - Interface-Features-Cpp

.

Uploaded by

gj8311
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views19 pages

4 - Interface-Features-Cpp

.

Uploaded by

gj8311
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Multiple Interface Inheritance,

Default & Static Methods,


Functional Interfaces
A deep dive into C++ polymorphism — from pure virtual contracts and
diamond inheritance to callables, concepts, and zero-overhead compile-
time dispatch.

ADVANCED C++ OBJECT-ORIENTED & GENERIC PROGRAMMING


Topic Classification
Prerequisites
Topic Name Multiple Interface
Inheritance, Default & Classes & Virtual Functions
Static Methods, Functional
Interfaces Understanding vtables and vptr mechanics

Category Object-Oriented & Generic Templates & RAII


Programming
Generic programming and resource management
Domain Core C++ / Polymorphism,
Inheritance, Type Erasure Smart Pointers

Topic Type Language feature + design Ownership semantics with unique_ptr / shared_ptr
construct

Recommended path: Abstract classes → pure virtual


Difficulty Advanced
→ multiple inheritance & virtual base →
CRTP/concepts → std::function/callables
Industry Relevance High — systems, game
engines, HFT, embedded,
large C++ frameworks
What Is It?
C++ has no interface keyword. An "interface" is an abstract class containing only pure virtual functions (virtual T f() = 0;) and a
virtual destructor. C++ supports multiple inheritance of such bases, giving multiple interface inheritance; ambiguity from shared
bases is controlled with virtual inheritance.

Core Concepts Key Terminology


Pure virtual, abstract class, vtable/vptr
Pure Virtual Default Methods Virtual destructor, diamond problem
Declares a contract — Non-pure virtual Virtual inheritance, CRTP
derived classes must functions with a body — Concept (C++20), std::function
implement reusable defaults
Functor, operator(), std::invoke

Static Methods Functional Interface Runtime polymorphism via vtables, compile-time


polymorphism via templates/concepts, and first-class
Belong to the class, not Any callable: free
behavior via callables — all without a built-in interface
an instance functions, lambdas,
keyword.
functors, std::function
Why Does It Exist?
Problems It Solves

1 Decouple callers from


concrete types

Callers depend only on the


abstract interface, not the
implementation

2 Fulfill several contracts at


once
A type can implement multiple
interfaces simultaneously

3 Store and pass behavior


Callables let behavior be treated as
first-class values

4 Solve the diamond problem


Virtual inheritance prevents
duplication of shared base
subobjects

Technical Driver Business Driver Industry Driver


Polymorphism with zero-overhead Pluggable, testable subsystems that Engines and frameworks expose
options via CRTP and concepts can be swapped independently abstract interfaces for plugins and
backends

Without a virtual destructor on an interface, delete base_ptr; to a derived object is undefined behavior — leaks and
memory corruption.
Evolution & History
C++98 1
Virtual functions, multiple & virtual inheritance,
function pointers

2 C++11
Lambdas, std::function, override/final, = default/=
delete
C++14 3
Generic lambdas — type-flexible inline callables

4 C++17
std::invoke, if constexpr, fold expressions

C++20 5
Concepts — named compile-time interfaces;
constrained templates with readable errors
6 C++23
std::move_only_function, deducing this (explicit object
params)

Previous Limits Current State & Future


Pure abstract classes incur vtable indirection. Templates gave Two complementary axes: runtime (abstract classes +
compile-time polymorphism but with cryptic errors — std::function) and compile-time (templates + concepts +
concepts named and constrained them readably. CRTP). Future: wider concept adoption; reflection (proposed)
to generate interface boilerplate.
How It Works
vtable Dispatch

A polymorphic object stores a hidden vptr to its class's vtable; a virtual call indexes the vtable to find the override. Multiple
inheritance gives multiple vptrs; cross-casts adjust the this pointer.

obj ──vptr──▶ [ vtable ]


├─ &Derived::f
├─ &Derived::g
└─ &~Derived (virtual dtor!)

Diamond + Virtual Inheritance Compile-Time Interface (Concepts)

A Without virtual: B and C each carry an A template<class T>


/\ subobject → D has TWO A's (ambiguous). concept Drawable = requires(T t) {
B C { [Link]() } -> std::same_as<void>;
\/ class B : virtual A {}; };
D class C : virtual A {}; → D has ONE shared A. void render(Drawable auto& d) {
[Link](); // checked at compile time
}

std::invoke(f, args...) calls a function pointer, lambda, functor, or


member pointer uniformly.
Types & Variants
Variant Mechanism Dispatch Use Case

Abstract class Pure virtual =0 Runtime (vtable) Plugin / backends

Default-bearing base Non-pure virtual + body Runtime Shared default behavior

CRTP class D : Base<D> Compile-time Zero-overhead


polymorphism

Concept (C++20) concept + requires Compile-time Constrained generics

std::function Type-erased wrapper Runtime (indirect) Store heterogeneous


callables

Lambda / functor operator() Often inlined Predicates, callbacks

Runtime Axis Compile-Time Axis


Abstract classes + std::function — maximum flexibility, vtable Templates + Concepts + CRTP — zero overhead, errors at
indirection cost compile time
Real-World Use Cases

Render Backend Abstraction Strategy via Callables Event System


Game Engine: Abstract IRenderer with HFT/Trading: Pluggable order-routing Embedded GUI:
pure virtual draw, virtual dtor, plus a logic on a hot path. Template parameter std::function<void(Event)> registry
default clear. Swap Vulkan/DirectX/Metal constrained by a concept, or a stored accepting lambdas/functors. Type
backends at runtime. Lesson: always functor — avoid std::function in the hot erasure stores heterogeneous callables.
declare a virtual destructor. path. Lesson: compile-time dispatch = no Lesson: mind std::function's allocation and
indirection = nanosecond latency. indirection cost.
Implementations & Examples
Example 1 — Multiple Interface Inheritance + Diamond Fix

struct Flyer { virtual ~Flyer() = default; virtual const char* move() = 0; };


struct Swimmer { virtual ~Swimmer() = default; virtual const char* move() = 0; };

struct Duck : Flyer, Swimmer {


const char* move() override { return "fly & swim"; } // resolves both
};

// Diamond shared base:


struct Base { int id = 0; virtual ~Base() = default; };
struct L : virtual Base {}; // virtual inheritance
struct R : virtual Base {};
struct D : L, R {}; // single shared Base subobject

Example 2 — Default Method + Static Factory Example 3 — Functional Interface

struct Greeter { using Transformer =


virtual ~Greeter() = default; std::function<std::string(std::string)>;
virtual std::string name() const = 0; // contract
virtual std::string greet() const { // default method std::string pipe(std::string s,
return "Hello, " + name(); std::initializer_list<Transformer> fns) {
} for (auto& f : fns) s = f(s);
static std::unique_ptr<Greeter> return s;
make(std::string n); // static factory }
};
// Compile-time alternative:
template<class F>
concept StringMap = requires(F f, std::string s) {
{ f(s) } -> std::convertible_to<std::string>;
};

std::function stores any matching callable at runtime; the concept enforces the same shape at compile time with no
overhead.
Hands-On Labs
LAB 1 — BEGINNER LAB 2 — ADVANCED

Plugin Interface Diamond & Concepts


Objective: Abstract ICodec with pure virtual encode/decode Objective: Build a diamond with and without virtual
and a default name(). inheritance; add a Drawable concept.

01
Object slicing when copying by value through base
Define the interface — always use references/pointers.

Virtual dtor, pure virtual encode/decode, default name()

02

Implement Base64Codec
Concrete class overriding the pure virtuals

03

Store via unique_ptr<ICodec>


Call through the base pointer

04

Validate
Polymorphic calls dispatch correctly; no leaks under sanitizer

Missing virtual dtor → ASan reports leak. Always add


virtual ~ICodec() = default;

01 02

Observe ambiguous-base error Fix with virtual inheritance


Build diamond without virtual inheritance first Add virtual keyword to intermediate bases

03 04

Write a requires concept Trigger a concept error


Constrain a render template with Drawable Pass a non-conforming type and read the readable error
Advantages & Disadvantages
Advantages Disadvantages

Multiple contract Dual dispatch Diamond Manual virtual


inheritance options complexity destructor
A type can fulfill Both runtime (vtable) Shared bases require Forgetting it causes
several interfaces and compile-time virtual inheritance undefined behavior —
simultaneously (concepts/CRTP) discipline no compiler
available enforcement

Flexible callables Readable std::function Template/concep


compile-time overhead t errors
std::function stores
interfaces
any callable — May heap-allocate for Still verbose despite
lambdas, functors, Concepts give clear, large captures; adds improvements in
function pointers named constraints on indirect call cost C++20
templates
Performance Considerations
Virtual Call Cost std::function Overhead
One indirection per call; inhibits May heap-allocate for large
inlining. In tight loops, prefer captures and adds an indirect
CRTP or concept-constrained call. Store small lambdas
templates for zero-overhead directly or use
static dispatch. std::move_only_function
(C++23).

Multiple Inheritance Cost


Adds pointer adjustment on cross-casts. Multiple vptrs per object with
multiple bases.

Benchmark with Google Benchmark; inspect with -O2 and check


whether virtual calls inline. Profile before optimizing — measure,
don't guess.
Scalability & Reliability

Scaling Safely
Abstract interfaces let large codebases add implementations
independently — the open/closed principle in action. New
backends, codecs, or strategies can be added without
modifying existing code.

Compile-time interfaces via concepts catch contract violations


before runtime, dramatically improving reliability in large teams
and codebases.

RAII + smart pointers (unique_ptr/shared_ptr) ensure


deterministic cleanup across polymorphic hierarchies — no
manual delete, no leaks.

Keep interfaces stateless and ownership explicit to


scale safely across modules and threads.
Design & Architectural Considerations

Performance-Critical
Apply CRTP and inlining

Hot Paths
Use templates + concepts

Runtime-Varying Types
Prefer abstract classes

Plugin Boundaries
Use runtime polymorphism

The choice between runtime and compile-time polymorphism is the central architectural decision in C++ interface design. Match
the mechanism to the context.

Always: Virtual Destructor Prefer Composition


On any base deleted polymorphically — no exceptions. Avoid deep multiple-inheritance lattices. Compose
virtual ~T() = default; behaviors rather than stacking inheritance levels.

Interface Segregation Storage vs Speed


Keep abstract classes small and single-purpose (ISP). One Use std::function for storage flexibility; raw templates for
contract per interface — don't bundle unrelated methods. speed. Don't pay for what you don't need.
Security Considerations

Missing Virtual Destructor


UB on polymorphic delete — memory corruption and leaks. Always add
virtual ~T() = default; to every interface.

Object Slicing
Passing interfaces by value silently drops derived state. Always pass by
reference or pointer — never by value.

Empty std::function
std::function invoked while empty throws std::bad_function_call. Always
check before calling in production code.

Unsafe Downcasting
Avoid casting away constness. Use dynamic_cast (not C-style) when
downcasting and always check the result for nullptr.

Validate inputs inside virtual methods — derived implementations


may not perform the same checks as the base contract assumes.
Best Practices
✅ Do's ❌ Don'ts
Virtual destructor Omit the virtual destructor

Declare virtual ~T() = default; on every interface Undefined behavior on polymorphic delete — no
exceptions

Mark overrides
Pass polymorphic types by value
Always use the override keyword on derived
implementations Object slicing silently corrupts derived state

Virtual inheritance for diamonds Overuse std::function in hot loops

Use virtual on intermediate bases sharing a common Allocation and indirection cost adds up at nanosecond
ancestor scale

Concepts/CRTP on hot paths Build deep MI lattices

Prefer zero-overhead compile-time dispatch where Prefer composition over stacking multiple inheritance levels
performance matters
dynamic_cast without checking
Pass by reference/pointer
Always check the result — a null pointer means the cast
Never pass polymorphic types by value failed

Smart pointers for ownership


Use unique_ptr/shared_ptr for polymorphic object
lifetimes
Common Mistakes & Pitfalls
Level Mistake Why It Happens Impact Correct Approach

Beginner No virtual destructor Don't know UB, leaks, memory virtual ~T() = default;
polymorphic-delete corruption
rule

Intermediate Passing interface by Forget reference Object slicing — Pass by ref/pointer


value semantics derived state lost always

Advanced Diamond without Misunderstand Ambiguous base, Virtual inheritance on


virtual subobject layout duplicate state shared bases

Production std::function in Ignore Latency spikes, CRTP / concept-


nanosecond hot path allocation/indirection missed SLAs constrained template
cost

1 2

Beginner Trap Intermediate Trap


Missing virtual destructor is the #1 C++ interface bug — Value semantics and polymorphism don't mix — slicing is
always add it first silent and deadly

3 4

Advanced Trap Production Trap


Diamond inheritance without virtual keyword duplicates std::function convenience hides real allocation and
base subobjects indirection costs
Comparison with Alternatives

Dimens Abstra CRTP Conce std::fu


ion ct pt nction
Class

Dispatc Runtim Compil Compil Runtim


h e e-time e-time e
(vtable) (indirec
t)

Overhe Indirecti Zero Zero Possibl


ad on e alloc

Polymo Subtyp Static Static Type


rphism e erasure

Error Runtim Compil Compil Runtim


timing e e e e
(readab
le)

Best fit Plugins Hot- Constra Store


path ined callable
generic s
s

No single mechanism wins — choose based on


whether types vary at runtime, whether you're on a
hot path, and whether you need to store
heterogeneous callables.
Learning Summary
Interfaces = Abstract Classes Default Methods Functional Interfaces
Pure virtuals + virtual destructor. Non-pure virtuals with a body — Callables: std::function, lambdas,
Multiple inheritance allowed. reusable shared behavior without functors. Concepts/CRTP as zero-
Diamonds need virtual inheritance. forcing overrides. overhead compile-time alternatives.

Mnemonic — "V-V-C": Virtual destructor always · Virtual inheritance for diamonds · Concepts/CRTP for speed

Cheat Sheet Interview Questions

=0 = pure virtual (must override)


Why must an interface How does virtual
virtual ~T() mandatory on every interface
have a virtual destructor? inheritance solve the
virtual inheritance kills the diamond diamond problem?
override/final for safety

Concepts constrain templates readably


CRTP vs virtual dispatch When does std::function
std::invoke unifies all callable invocations
— trade-offs? allocate?

Next Topics: Type erasure idioms · C++20 ranges & concepts · CRTP patterns · Interface vs Abstract Class

You might also like