Project 7.08 Section 7 ⚡ Embedded Relevance: High Constructor Exceptions Resource Leak noexcept RAII Invariants

7.08 Constructor Invariants, Destructor Guarantees & Partial Initialization Leaks

Executive Summary: Deep dive into throwing exceptions from class constructors. We examine the classic C++ memory leak hazard when initializing multiple heap members in a constructor, analyze compiler destructor guarantees, and implement safe noexcept construction patterns.

💻 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

📐 Person Invariant Validation & std::invalid_argument
+ Public - Private # Protected
<<class>> Person Entity
-name : std::string
-age : int32_t
+Person(name: string, age: int)
+setName(name: string) : void[throws std::invalid_argument]
+setAge(age: int) : void[throws std::invalid_argument]
+getName() : std::string const
+getAge() : int32_t const

📚 3. Core C++ Concepts Deep-Dive

1. The Partial Construction Problem

Consider a class that allocates two raw pointer resources in its constructor:

📚 Concept Implementation
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:

💡 Production-Ready Refactor
#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.

Q1. If a constructor allocates Resource 1 with raw new, and then Resource 2 throws std::bad_alloc, what happens to Resource 1?
A Resource 1 is leaked because the class destructor is never called for an object that failed construction
B Resource 1 is automatically garbage collected
C The runtime calls the destructor on half-built objects
D The compiler rewires the pointer to null
Detailed Explanation: Because the constructor did not finish, the object was never fully created, so its destructor does not execute. Any raw resources allocated before the throw are leaked unless managed by RAII smart wrappers.
Q2. What is the primary benefit of declaring constructors and methods noexcept in C++?
A It signals that the function will never throw, allowing the compiler to omit exception unwinding tables and perform optimal move semantics
B It makes the function run in privileged CPU mode
C It converts the class into a template
D It forces parameters to be passed by reference
Detailed Explanation: 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.
Q3. How does RAII (std::unique_ptr) solve constructor resource leaks?
A Member sub-objects that have already completed construction have their individual destructors executed automatically when an exception is thrown
B It converts raw pointers into integers
C It allocates memory in battery-backed SRAM
D It prevents constructors from taking arguments
Detailed Explanation: C++ guarantees that all fully constructed sub-objects and base classes will have their destructors called if a constructor later throws. std::unique_ptr destructors automatically free their held pointers.
Q4. What happens if a function marked noexcept throws an exception?
A std::terminate() is immediately invoked, halting the program
B The exception is converted into a compiler warning
C The function retries execution from the beginning
D The catch block in main catches it as a generic exception
Detailed Explanation: If an exception escapes a noexcept function, the runtime calls std::terminate() immediately without unwinding remaining stack frames.