C++

C++ in Embedded Systems

Every chapter in this domain is real, useful C++ — but on a microcontroller with a few KB of RAM and hard real-time deadlines, not every feature is a good fit. This closing chapter is the honest map: what a typical bare-metal C++ codebase avoids from everything covered so far, what it happily keeps, and why — closing the loop back to Embedded C, which covers the same hardware-constrained mindset for plain C.

Quick reference
What it is
The subset of C++ commonly used on bare-metal/RTOS targets, and the specific features commonly avoided or restricted, with concrete reasons for each.
Mental model
Desktop C++ optimizes for programmer productivity at some cost in predictability; embedded C++ makes the opposite trade — predictable timing and memory footprint, even at some cost to convenience.
Key numbers
Exception-handling machinery alone can add tens of KB to a binary — often a meaningful fraction of a microcontroller's entire flash budget.
Reach for it when
Deciding, up front, which C++ features a new embedded project's style guide should allow — a decision worth making deliberately, not by accident.

Why exceptions are often disabled

Exception Handling covered the mechanism; here's why many embedded teams compile with exceptions turned off entirely (-fno-exceptions on GCC):

The common replacement: return codes or std::optional-style "might not have a value" types, checked explicitly — closer to C's own error-handling convention, chosen deliberately for the same predictability reasons.

Why heavy dynamic allocation is limited

STL containers and smart pointers both lean on heap allocation — exactly the concern Embedded C's Dynamic Memory raises about malloc, still true here:

Common replacements: fixed-size containers sized at compile time, memory pools with a bounded number of fixed-size blocks, and std::array (a stack-allocated, fixed-size STL type not covered earlier precisely because it has none of these issues) in place of std::vector.

RTTI — usually disabled too

RTTI (Run-Time Type Information) is what powers dynamic_cast and typeid — asking "what concrete type is actually behind this base-class pointer" at runtime. Like exceptions, it adds real code size and is very commonly disabled (-fno-rtti) on constrained targets. The virtual function dispatch that makes polymorphism work does not depend on RTTI at all — that part keeps working fine with RTTI turned off.

What carries over cleanly

Most of this domain isn't actually the problem — the features above are a short, specific list, not "avoid C++." What's commonly kept, freely:

Classes & encapsulation
Zero runtime cost — a class compiles down to essentially the same code as a struct plus plain functions.
RAII
Genuinely valuable on embedded specifically — automatic peripheral setup/teardown with no runtime cost.
Virtual functions
A small, predictable, constant-time cost (one extra pointer indirection) — commonly used for driver interfaces.
Templates
Zero runtime cost, since everything resolves at compile time — genuinely popular for embedded, though watch total code size across many instantiations.
std::unique_ptr
Zero overhead versus a raw pointer — commonly allowed even where shared_ptr and heavy STL containers are not.

Real-world applications

Gotchas

Field notes