7.08 Constructor Invariants, Destructor Guarantees & Partial Initialization Leaks
💻 1. Annotated Source Code
#include <iostream>> #include <stdexcept> #include "Person.h" using namespace std; int main() { try { Person person1("Bob"); Person person2("Sally"); //Person person3("John"); Person person4("William"); cout << person1.getName() << endl; cout << person2.getName() << endl; //cout << person3.getName() << endl; cout << person4.getName() << endl; } catch (const runtime_error& err) { cout << err.what() << endl; } }
#ifndef PERSON_H #define PERSON_H #include <string> using namespace std; class Person { public: Person(string name); string getName() const noexcept; void setName(string name); private: string name; }; #endif
#include "Person.h" #include <stdexcept> using namespace std; Person::Person(string name) { setName(name); } string Person::getName() const noexcept { return name; } void Person::setName(string name) { if (name != "John") { this->name = name; } else { throw runtime_error("John? That guy is too ugly for an object!"); } }
📐 2. Architecture & UML Class Model
📚 3. Core C++ Concepts Deep-Dive
1. The Partial Construction Problem
Consider a class that allocates two raw pointer resources in its constructor:
MyClass::MyClass() { ptr1 = new ResourceA(); // Succeeded ptr2 = new ResourceB(); // THROWS EXCEPTION! }
Because the constructor never finished, ~MyClass() will never be called. ptr1 is leaked forever! This is why raw pointers in constructors violate basic exception safety.
2. Solving via RAII Smart Wrappers
Using std::unique_ptr for member variables guarantees that if a later initialization step throws, already initialized member smart pointers have their destructors called automatically.
⚡ 4. Embedded Systems & Hardware Reality
1. noexcept Constructor Guarantees in Firmware
In embedded firmware, declaring constructors noexcept tells the compiler that no unwind tables are needed, enabling compiler optimizations like vector reallocation via move instead of copy.
2. Placement-New and In-Place Static Construction
For systems that cannot afford dynamic allocation, placement-new allows constructing an object inside a statically allocated byte buffer (alignas(T) std::byte buffer[sizeof(T)]) with deterministic placement.
💡 5. Production-Ready Embedded Refactoring
Here is how to design exception-safe classes with guaranteed cleanup and noexcept construction:
#include <cstdint> #include <string_view> #include <array> class FirmwareNode { private: std::array<char, 16> node_name_{}; uint8_t node_id_{0}; public: // Fully noexcept constructor; zero possibility of memory leak or throwing constexpr FirmwareNode(uint8_t id, std::string_view name) noexcept : node_id_(id) { size_t len = name.size() < 15 ? name.size() : 15; for (size_t i = 0; i < len; ++i) { node_name_[i] = name[i]; } node_name_[len] = '\0'; } constexpr uint8_t get_id() const noexcept { return node_id_; } constexpr std::string_view get_name() const noexcept { return node_name_.data(); } };
📝 Knowledge Verification Quiz
Test your understanding of the C++ concepts and embedded microcontroller trade-offs covered in this guide. Click any option for instant feedback.
noexcept promises that no exceptions will escape. This removes unwind code generation overhead and allows STL containers (like std::vector) to safely use fast move operations.
std::unique_ptr destructors automatically free their held pointers.
noexcept function, the runtime calls std::terminate() immediately without unwinding remaining stack frames.