Home CodingThe “Cost” of C++ – Virtual Functions, Vtables, and Memory

The “Cost” of C++ – Virtual Functions, Vtables, and Memory

by dnaadmin

In the semiconductor and embedded world, there is a long-standing myth that C++ is “too heavy” for bare-metal systems. As a System Architect, your job is to debunk this by explaining exactly where the overhead lies. The most common interview question in this domain is: “How do virtual functions work, and what is their impact on performance and memory?”


1. The Mechanics: The Vtable and the Vptr

In C++, Polymorphism (deciding which function to call at runtime) is implemented using a Virtual Method Table (Vtable).

  • The Vtable: For every class that has at least one virtual function, the compiler creates a static array of function pointers.

  • The Vptr (Virtual Pointer): Every instance of that class gets a hidden pointer (usually 4 or 8 bytes) that points to the Vtable.

The Execution Flow:

  1. Follow the Vptr from the object instance to the Vtable.

  2. Look up the function pointer at the correct offset.

  3. Jump to that address.


2. The Hidden Costs: Memory and Speed

When an interviewer asks about the “cost,” they are looking for three specific points:

A. Memory Overhead (RAM/Flash)

  • Flash: Each class with virtual functions adds a Vtable to the .rodata segment.

  • RAM: Every object instance is now larger by 4 or 8 bytes because of the Vptr. In a system with thousands of small objects (like network packets), this adds up.

B. Execution Latency

A virtual call involves an extra memory load (dereferencing the Vptr) and an indirect jump. On modern CPUs, this can cause a Branch Prediction Miss, stalling the pipeline for several cycles.

C. Code Optimization (Inlining)

This is the “Architect’s Insight.” The biggest cost isn’t the jump; it’s that the compiler cannot inline a virtual function because it doesn’t know which function will be called until the program is actually running. This prevents many powerful compiler optimizations.


3. The “No Exceptions” Rule: -fno-exceptions

In many embedded C++ projects, the first thing an architect does is disable Exceptions and RTTI (Run-Time Type Information).

  • Why? Exception handling requires a massive “unwinding” library that bloats the binary size and adds non-deterministic timing.

  • The Interview Answer: “We use C++ for its type safety and RAII, but we disable exceptions (-fno-exceptions) and RTTI (-fno-rtti) to keep the binary lean and deterministic.”


4. Architect’s “Pro” Question: The “Virtual” Destructor

Q: Why must a base class have a virtual destructor if it has virtual functions?

A: If you delete a derived object through a base class pointer (Base *p = new Derived(); delete p;), and the destructor is NOT virtual, only the Base destructor is called. This leads to a Memory Leak because the Derived parts of the object are never cleaned up.


5. Summary Table: C++ Features in Embedded

Feature Cost Recommendation
Classes/Structs Zero Use freely (same as C).
Templates Flash Bloat Use carefully; can lead to “code explosion.”
Virtual Functions Memory + Latency Use for abstraction, but not in high-frequency loops.
Exceptions Massive Bloat Disable in bare-metal systems.
Name Mangling None Use extern "C" when calling from C code.

Architect’s Interview Tip

If asked about C++ vs C, mention Zero-Cost Abstractions. Explain that features like std::array, templates, and constexpr often result in code that is just as fast (or faster) than manual C, while providing better type safety. This shows you aren’t an “Anti-C++” dinosaur, but a pragmatist who knows which tools to use.


In the next article, we return to the bit-level: Bit Manipulation, Bit-Fields, and the Endianness Trap.

Ready for Article 7?

You may also like

Leave a Comment