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.
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.
| Path | Role | Key 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 |
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) |
+------------------+
OOP Concepts — At a Glance
🔒 Encapsulation
Private fields availableSeats and totalRevenue in Flight are exposed read-only through getters and mutated exclusively via operator+=.
🏗️ Constructors
Initializer lists and std::move throughout. Derived classes chain to base via Ticket(...) syntax. Destructor handles dynamic memory cleanup.
➕ Operator Overloading
operator+= books a ticket (updates seats + revenue). operator[] provides safe indexed access to the passenger manifest.
🧬 Inheritance & Friend
EconomyTicket and BusinessTicket override pure virtuals. SecurityCheck is granted friend access to Passenger's private passport field.
🔁 Polymorphism
Flight iterates vector<Ticket*> calling virtual methods — the runtime resolves the correct derived implementation automatically.
🧩 Templates
WaitingList<T> wraps std::queue<T> generically. Instantiated as WaitingList<string>, but type-compatible with any data type.
📁 File I/O & RTTI
ofstream / ifstream for persistence. dynamic_cast reconstructs the full polymorphic ticket hierarchy from flat text on load.
📦 STL
std::vector<Ticket*> for dynamic passenger storage with O(1) amortized append. std::queue<T> for FIFO standby management.
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.
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 };
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.
// 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(); }
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& 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 }
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.
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; }
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.
// 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 }
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.
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;
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.
// 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)); } }
STL Containers
| Container | Used In | Purpose | Complexity |
|---|---|---|---|
| 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 |