Project 6.05 Section 6 ⚡ Embedded Relevance: Core Composition HAS-A Destructors RAII Hardware Cleanup

6.05 Object Composition (HAS-A), Destructor Chains & Deterministic Hardware Teardown

Executive Summary: Building composite objects through composition (HAS-A relationships). We examine constructor and destructor execution chains, and demonstrate how composite RAII classes automatically power down peripherals in reverse order of initialization upon scope exit.

💻 1. Annotated Source Code

#include <iostream>
#include "IceCreamSundae.h"
using namespace std;

int main() {

	IceCreamSundae basicVanilla("Vanilla");
	basicVanilla.printSundae();

	IceCreamSundae deluxeChocolate("Chocolate");
	deluxeChocolate.addTopping("Whipped Cream");
	deluxeChocolate.addTopping("Sprinkles");
	deluxeChocolate.addTopping("Cherries");

	cout << endl;
	deluxeChocolate.printSundae();

	return 0;
}
#ifndef ICE_CREAM_SUNDAE_H
#define ICE_CREAM_SUNDAE_H

#include <string>
#include <vector>
using namespace std;

class IceCreamSundae {
	public:
		IceCreamSundae(string flavor);
		void addTopping(string topping);
		string getFlavor() const;
		vector<string> getToppings() const;
		void printSundae() const;

	private:
		string flavor;
		vector<string> toppings;
};

#endif
#include "IceCreamSundae.h"
#include <iostream>
using namespace std;

IceCreamSundae::IceCreamSundae(string flavor) {
	this->flavor = flavor;
}

void IceCreamSundae::addTopping(string topping) {
	toppings.push_back(topping);
}

string IceCreamSundae::getFlavor() const {
	return flavor;
}

vector<string> IceCreamSundae::getToppings() const {
	return toppings;
}

void IceCreamSundae::printSundae() const {
	cout << "Sundae flavor: " << flavor << endl;
	cout << "Toppings: " << endl;
	if (toppings.empty()) {
		cout << "None";
	}
	else {
		for (string topping : toppings) {
			cout << topping << ", ";
		}
	}

	cout << endl;
}

📐 2. Architecture & UML Class Model

📐 IceCreamSundae Aggregation & Dynamic Toppings Model
+ Public - Private # Protected
<<class>> IceCreamSundae Aggregator
-numScoops : int32_t
-flavor : std::string
-toppings : std::vector<std::string>
+IceCreamSundae(flavor: string, numScoops: int)
+addTopping(topping: string) : void
+printSundae() : void const
+getFlavor() : std::string const
+getNumScoops() : int32_t const

📚 3. Core C++ Concepts Deep-Dive

1. Object Composition (HAS-A)

Composition embeds one class instance inside another as a member variable. The composite object controls the lifetime of its member sub-objects.

2. Constructor & Destructor Execution Order

  • Construction: Member objects are constructed first (in declaration order), followed by the enclosing class constructor body.
  • Destruction: The enclosing class destructor executes first, followed by member destructors in reverse order of declaration.

⚡ 4. Embedded Systems & Hardware Reality

1. Safe Hardware Peripheral Teardown Chains

In power-sensitive embedded systems, RAII composite drivers leverage reverse-order destruction to power down sub-peripherals in strictly safe sequences (e.g. disabling DMA $\rightarrow$ disabling SPI $\rightarrow$ gating peripheral clock).

💡 5. Production-Ready Embedded Refactoring

RAII composite sensor subsystem with automatic hardware teardown:

💡 Production-Ready Refactor
#include <cstdint>

struct SpiClockGate {
    SpiClockGate() noexcept { /* RCC->APB2ENR |= SPI1_EN; */ }
    ~SpiClockGate() noexcept { /* RCC->APB2ENR &= ~SPI1_EN; Disable clock on exit! */ }
};

struct GpioChipSelect {
    GpioChipSelect() noexcept { /* Set CS Pin LOW (Active) */ }
    ~GpioChipSelect() noexcept { /* Set CS Pin HIGH (Idle) */ }
};

class SpiTransactionScope {
private:
    SpiClockGate clock_gate_;      // Constructed 1st; Destroyed 2nd
    GpioChipSelect chip_select_;    // Constructed 2nd; Destroyed 1st
public:
    void write_data(uint8_t byte) noexcept { /* SPI1->DR = byte; */ }
};

📝 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. In what order are member sub-objects destroyed when an enclosing class instance goes out of scope?
A In the exact reverse order of their declaration in the class definition
B In the exact order of declaration
C Alphabetical order
D Simultaneously in 1 clock cycle
Detailed Explanation: C++ guarantees that destructors are invoked in reverse order of member declaration, ensuring proper symmetric teardown of dependent resources.
Q2. What relationship does 'composition' represent in Object-Oriented Design?
A A 'HAS-A' relationship where one class contains instances of other classes as member components
B An 'IS-A' inheritance relationship
C A dynamic link relationship
D A friend class relationship
Detailed Explanation: Composition represents a 'HAS-A' relationship (e.g., a Car HAS-A Engine), where the outer class owns and manages the lifecycle of the inner component.
Q3. How does RAII (Resource Acquisition Is Initialization) prevent peripheral power drain in battery devices?
A Peripheral clocks and GPIOs are acquired in constructors and automatically powered down in destructors when leaving scope
B It turns off the microcontroller battery
C It decreases the baud rate
D It converts integers to floating point
Detailed Explanation: RAII ties hardware peripheral power states to object lifetimes, guaranteeing that peripherals are safely disabled as soon as their enclosing scope exits.
Q4. If class A contains member class B, when is B's constructor executed relative to A's constructor body?
A B's constructor is fully executed BEFORE A's constructor body begins
B B's constructor is executed after A's constructor body finishes
C B's constructor is ignored
D Only when explicitly called
Detailed Explanation: All member sub-objects are fully constructed before the body of the enclosing class constructor starts execution.