10.03 Class Hierarchies, Member Initializers & Deterministic Object Pooling
💻 1. Annotated Source Code
#include <iostream> #include <string> #include <vector> #include "Player.h" #include "Warrior.h" #include "Priest.h" #include "Mage.h" using namespace std; void printMainMenu(); void printRaceMenu(); Race getRace(int raceNum); Player* createPlayer(string name, int typeNum, int raceNum); void printAll(const vector<Player*>& playerList); void doCleanup(vector<Player*>& playerList); int main() { int choice; int raceNum; string playerName; vector<Player*> playerList; printMainMenu(); cin >> choice; cin.get(); while (choice != 0) { cout << "What would you like to name your character?" << endl; getline(cin, playerName); printRaceMenu(); cin >> raceNum; cin.get(); Player* tempPlayer = createPlayer(playerName, choice, raceNum); playerList.push_back(tempPlayer); printMainMenu(); cin >> choice; cin.get(); }//end while printAll(playerList); doCleanup(playerList); cout << "Program done!" << endl; return 0; } void printMainMenu() { cout << "\nChoose a class:\n" << "\t1 - Warrior\n" << "\t2 - Priest\n" << "\t3 - Mage\n" << "\t0 - Finish\n"; }//end printMainMenu void printRaceMenu() { cout << "Choose a race:\n" << "\t1 - Human\n" << "\t2 - Elf\n" << "\t3 - Dwarf\n" << "\t4 - Orc\n" << "\t5 - Troll\n"; }//end printRaceMenu Race getRace(int raceNum) { switch (raceNum) { case 1: return Race::HUMAN; case 2: return Race::ELF; case 3: return Race::DWARF; case 4: return Race::ORC; case 5: return Race::TROLL; default: return Race::HUMAN; } }//end getRace Player* createPlayer(string name, int typeNum, int raceNum) { Race race = getRace(raceNum); switch (typeNum) { case 1: return new Warrior(name, race); case 2: return new Priest(name, race); case 3: return new Mage(name, race); default: return nullptr; } } void printAll(const vector<Player*>& playerList) { for (const Player* player : playerList) { cout << "My name is "<<player->getName() << ". I'm a " << player->whatRace() << " and my attack is: " << player->attack() << endl; } } void doCleanup(vector<Player*>& playerList) { for (Player* player : playerList) { delete player; } playerList.clear(); }
#ifndef PLAYER_H #define PLAYER_H #include <string> using namespace std; enum class Race { HUMAN, ELF, DWARF, ORC, TROLL }; class Player { public: Player(string name, Race race, int hitPoints, int magicPoints); virtual ~Player() = default; string getName() const; Race getRace() const; string whatRace() const; int getHitPoints() const; int getMagicPointers() const; void setName(string name); void setRace(Race race); void setHitPoints(int hitPoints); void setMagicPoints(int magicPoints); virtual string attack() const = 0; private: string name; Race race; int hitPoints; int magicPoints; }; #endif
#include "Player.h" Player::Player(string name, Race race, int hitPoints, int magicPoints) : name(name), race(race), hitPoints(hitPoints), magicPoints(magicPoints){ } string Player::getName() const { return name; } Race Player::getRace() const { return race; } string Player::whatRace() const { switch(race) { case Race::HUMAN: return "Human"; case Race::ELF: return "Elf"; case Race::DWARF: return "Dwarf"; case Race::ORC: return "Orc"; case Race::TROLL: return "Troll"; default: return "Unknown"; } } int Player::getHitPoints() const { return hitPoints; } int Player::getMagicPointers() const { return magicPoints; } void Player::setName(string name) { this->name = name; } void Player::setRace(Race race) { this->race = race; } void Player::setHitPoints(int hitPoints) { this->hitPoints = hitPoints; } void Player::setMagicPoints(int magicPoints) { this->magicPoints = magicPoints; }
#ifndef MAGE_H #define MAGE_H #include "Player.h" class Mage : public Player { public: Mage(string name, Race race) : Player(name, race, 150, 150) {} string attack() const override { return "I will crush you with the power of my arcane missiles!"; } }; #endif
// File not found: section_10/RPGProject/RPGProject/Mage.cpp
#ifndef WARRIOR_H #define WARRIOR_H #include "Player.h" class Warrior : public Player { public: Warrior(string name, Race race) : Player(name, race, 200, 0) {} string attack() const override { return "I will destroy you with my sword, foul demon!"; } }; #endif
// File not found: section_10/RPGProject/RPGProject/Warrior.cpp
#ifndef PRIEST_H #define PRIEST_H #include "Player.h" class Priest : public Player { public: Priest(string name, Race race) : Player(name, race, 100, 200) {} string attack() const override { return "I will assault you with Holy Wrath!"; } }; #endif
// File not found: section_10/RPGProject/RPGProject/Priest.cpp
📐 2. Architecture & UML Class Model
📚 3. Core C++ Concepts Deep-Dive
1. Member Initializer Lists
Initializing member variables in constructor initialization lists directly initializes members rather than default-constructing and then assigning, eliminating redundant operations.
📐 RPG Class Hierarchy UML Architecture
2. Virtual Destructors
When deleting a derived class object through a base class pointer (Base* ptr = new Derived(); delete ptr;), the base class destructor must be virtual; otherwise, the derived class destructor is not called, causing silent resource leaks.
⚡ 4. Embedded Systems & Hardware Reality
1. Static Object Pooling vs Dynamic Heap Allocation
In safety-critical avionics and medical devices (DO-178C / IEC 62304), dynamic heap allocation during runtime is prohibited due to fragmentation and non-deterministic allocation latency. Pre-allocating objects in a Fixed-Block Object Pool guarantees $O(1)$ allocation time and zero fragmentation.
💡 5. Production-Ready Embedded Refactoring
Fixed-capacity deterministic static object pool:
#include <cstdint> #include <array> template <typename T, size_t Capacity> class StaticObjectPool { private: alignas(T) uint8_t storage_[Capacity][sizeof(T)]; std::array<bool, Capacity> in_use_{}; public: template <typename... Args> T* allocate(Args&&... args) noexcept { for (size_t i = 0; i < Capacity; ++i) { if (!in_use_[i]) { in_use_[i] = true; return new (&storage_[i]) T(std::forward<Args>(args)...); // Placement new } } return nullptr; // Pool exhausted (predictable failure!) } };
📝 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.
delete base_ptr performs static binding, calling only the base destructor and leaving derived members uncleaned.