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
virtualfunction, 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:
-
Follow the Vptr from the object instance to the Vtable.
-
Look up the function pointer at the correct offset.
-
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
.rodatasegment. -
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?
