📄 Technical Documentation · v1.0

Airline Flight
Management System

A complete terminal-based flight management engine demonstrating all core Object-Oriented Programming paradigms in C++17, including runtime polymorphism, generic programming, friend class mechanisms, and file serialization.

⚙️ C++17
🛠️ CLion / CMake
👤 Bonaccorsi David
📅 2025 – 2026
01

Project Architecture

The codebase is organized into three functional modules, each with a distinct responsibility. This separation ensures that domain logic, core engine behavior, and auxiliary services remain decoupled and independently maintainable.

PathRoleKey Files
entities/ Core domain models and ticket hierarchy Ticket EconomyTicket BusinessTicket Passenger
core/ Flight engine and generic data structures Flight WaitingList<T>
services/ Independent operational logic SecurityCheck
main.cpp Entry point, interactive terminal menu do-while / switch-case
02

Class Diagram

The inheritance hierarchy is rooted in the abstract Ticket class. All polymorphic dispatch happens through vector<Ticket*> inside Flight.

         +-------------------------------+
         |        <<abstract>>           |
         |           Ticket              |
         |-------------------------------|
         | # passengerName : string      |
         | # flightNumber  : string      |
         | # basePrice     : float       |
         |-------------------------------|
         | + calculateFinalPrice() = 0   |
         | + printBoardingPass()   = 0   |
         | + virtual ~Ticket()           |
         +-------------------------------+
                       ▲
          _____________|_____________
         |                           |
+--------------------+   +------------------------+
|   EconomyTicket    |   |    BusinessTicket      |
|--------------------|   |------------------------|
|- baggageAllowance  |   |- loungeAccess : bool   |
|+ calculateFinal...()|  |- loyaltyPoints : int   |
|+ printBoarding...() |  |+ calculateFinal...()   |
+--------------------+   |+ printBoarding...()    |
                          +------------------------+

+------------------+  friend ◁───▷  +---------------------+
|    Passenger     |               |    SecurityCheck    |
|------------------|               |---------------------|
|- passportNumber  |               |+ verifyIdentity()   |
|- securityCleared |               +---------------------+
+------------------+

+------------------+  contains ──▷  +---------------------+
|     Flight       |               |  vector<Ticket*>    |
|------------------|               +---------------------+
|- availableSeats  |
|- totalRevenue    |
|+ operator+=      |
|+ operator[]      |
+------------------+

+------------------+
|  WaitingList<T>  |   // Generic Template
|------------------|
|- queue<T>        |
|+ add(T item)     |
+------------------+
03

OOP Concepts — At a Glance

Concept 01

🔒 Encapsulation

Private fields availableSeats and totalRevenue in Flight are exposed read-only through getters and mutated exclusively via operator+=.

Concept 02

🏗️ Constructors

Initializer lists and std::move throughout. Derived classes chain to base via Ticket(...) syntax. Destructor handles dynamic memory cleanup.

Concept 03

➕ Operator Overloading

operator+= books a ticket (updates seats + revenue). operator[] provides safe indexed access to the passenger manifest.

Concept 04

🧬 Inheritance & Friend

EconomyTicket and BusinessTicket override pure virtuals. SecurityCheck is granted friend access to Passenger's private passport field.

Concept 05

🔁 Polymorphism

Flight iterates vector<Ticket*> calling virtual methods — the runtime resolves the correct derived implementation automatically.

Concept 06

🧩 Templates

WaitingList<T> wraps std::queue<T> generically. Instantiated as WaitingList<string>, but type-compatible with any data type.

Concept 07

📁 File I/O & RTTI

ofstream / ifstream for persistence. dynamic_cast reconstructs the full polymorphic ticket hierarchy from flat text on load.

Concept 08

📦 STL

std::vector<Ticket*> for dynamic passenger storage with O(1) amortized append. std::queue<T> for FIFO standby management.

04

Encapsulation

In Flight, critical state fields are declared private. External code cannot read them directly — they are exposed via getters and mutated only through operator+=, which enforces business rules (seat availability check) before any change occurs.

Flight.h
class Flight {
private:
    int   availableSeats;   // Cannot be modified directly from outside
    float totalRevenue;     // Cannot be modified directly from outside

public:
    int   getAvailableSeats() const;  // Read-only access
    float getTotalRevenue()  const;  // Read-only access

    Flight& operator+=(Ticket* newTicket);  // Only mutation point
};
05

Constructors

All classes use explicit parameterized constructors with initializer lists. std::move avoids unnecessary string copies. Derived classes chain the base constructor via the Ticket(...) initializer syntax.

Ticket.cpp / EconomyTicket.cpp / Flight.cpp
// Base constructor — initializer list + std::move for efficiency
Ticket::Ticket(string name, string flight, float price)
    : passengerName(std::move(name)),
      flightNumber(std::move(flight)),
      basePrice(price) {}

// Derived constructor — chains to Ticket(...)
EconomyTicket::EconomyTicket(string name, string flight, float price, int baggage)
    : Ticket(std::move(name), std::move(flight), price),
      baggageAllowance(baggage) {}

// Destructor — releases dynamic memory allocated in vector<Ticket*>
Flight::~Flight() {
    for (const Ticket* t : tickets) delete t;
    tickets.clear();
}
06

Operator Overloading

Two operators are overloaded in Flight: operator+= encapsulates the full booking transaction (seat check, revenue update), and operator[] provides array-like access to the passenger manifest with bounds checking.

Flight.cpp
Flight& Flight::operator+=(Ticket* newTicket) {
    if (availableSeats > 0) {
        tickets.push_back(newTicket);
        availableSeats--;
        totalRevenue += newTicket->calculateFinalPrice();  // Polymorphic call
        cout << "Success: Ticket booked for " << newTicket->getName();
    } else {
        cout << "Error: Flight fully booked!";
    }
    return *this;
}

Ticket* Flight::operator[](const int index) {
    if (index >= 0 && index < (int)tickets.size())
        return tickets[index];
    return nullptr;  // Boundary-safe
}
07

Inheritance & Friend Classes

EconomyTicket and BusinessTicket inherit from the abstract Ticket base, each implementing class-specific pricing logic. The friend class mechanism grants SecurityCheck direct access to Passenger's private passportNumber without exposing it to the public API.

Passenger.h / SecurityCheck.cpp
class Passenger {
private:
    string passportNumber;       // Private — not accessible externally
    friend class SecurityCheck; // Exception: SecurityCheck has direct access
};

// SecurityCheck.cpp — accesses private field directly
bool SecurityCheck::verifyIdentity(Passenger& p, const string& presented) {
    if (p.passportNumber == presented) {   // Direct private access!
        p.securityCleared = true;
        return true;
    }
    return false;
}
08

Polymorphism

Ticket declares two pure virtual functions, making it abstract. Flight stores a vector<Ticket*> and calls these methods without knowing the concrete type — the C++ vtable resolves to the correct implementation at runtime.

Ticket.h / Flight.cpp
// Abstract interface — forces derived classes to implement
class Ticket {
public:
    virtual float calculateFinalPrice() const = 0;
    virtual void  printBoardingPass()  const = 0;
    virtual ~Ticket() = default;
};

// Polymorphic dispatch in Flight — type resolved at runtime
for (Ticket* t : tickets) {
    totalRevenue += t->calculateFinalPrice();
    // Calls EconomyTicket::calculateFinalPrice() or
    // BusinessTicket::calculateFinalPrice() depending on real type
}
09

Generic Programming — Templates

WaitingList<T> is a generic wrapper around std::queue<T>. A single implementation works with any type — string, int, Passenger* — with full compile-time type safety.

WaitingList.h / main.cpp
template <typename T>
class WaitingList {
private:
    queue<T> elements;
    string   listName;
public:
    explicit WaitingList(string name) : listName(std::move(name)) {}
    void add(T item) { elements.push(item); }
};

// main.cpp — instantiated with string
WaitingList<string> standbyList("Standby Queue");
standbyList.add("Alice Johnson");

// Could equally be:
// WaitingList<Passenger*> waitPassengers;
// WaitingList<int>        waitIds;
10

File I/O & RTTI

Data is persisted between runs via ofstream / ifstream. On load, dynamic_cast (RTTI) is used to detect the concrete ticket type encoded in the file and reconstruct the correct derived object, preserving the polymorphic hierarchy.

Flight.cpp
// Save — detect concrete type with dynamic_cast
void Flight::saveToFile(const string& filename) const {
    ofstream out(filename);
    out << availableSeats << '\n' << totalRevenue << '\n';
    for (Ticket* t : tickets) {
        if      (dynamic_cast<EconomyTicket*>(t))  out << 'E\n';
        else if (dynamic_cast<BusinessTicket*>(t)) out << 'B\n';
        out << t->getName() << '\n';
    }
}

// Load — reconstruct hierarchy from flat file
void Flight::loadFromFile(const string& filename) {
    ifstream in(filename);
    in >> availableSeats >> totalRevenue;
    char type; string name;
    while (in >> type) {
        getline(in, name);
        if      (type == 'E') tickets.push_back(new EconomyTicket(name, flightNumber, 100.0f, 20));
        else if (type == 'B') tickets.push_back(new BusinessTicket(name, flightNumber, 300.0f, true, 500));
    }
}
11

STL Containers

ContainerUsed InPurposeComplexity
std::vector<Ticket*> Flight Dynamic passenger list — supports polymorphic dispatch via pointers O(1) amortized append
std::queue<T> WaitingList<T> FIFO standby queue — ensures First-In-First-Out ordering for waitlisted passengers O(1) push / pop