08-real-world-and-interviews.md

Module 08: Real-World OOP & Interview Prep

Goal: Tie everything together with a real project, UML basics, and comprehensive interview Q&A. Time: 2 days of focused study Prerequisites: All previous modules


Table of Contents

  1. Real-World Project: Library Management System
  2. UML Class Diagrams
  3. OOP Design Exercise: Design a Parking Lot
  4. 100+ Interview Questions — Quick Fire
  5. Coding Challenges
  6. Final Checklist

1. Library Management System

A complete project demonstrating ALL OOP concepts together.

#include <iostream> #include <vector> #include <memory> #include <map> #include <chrono> #include <algorithm> using namespace std; // ========== INTERFACES (ISP) ========== class ISearchable { public: virtual vector<string> search(const string& query) const = 0; virtual ~ISearchable() = default; }; class IBorrowable { public: virtual bool borrow(int userId) = 0; virtual bool returnItem() = 0; virtual bool isAvailable() const = 0; virtual ~IBorrowable() = default; }; class INotifiable { public: virtual void notify(const string& message) = 0; virtual ~INotifiable() = default; }; // ========== ENTITIES (SRP) ========== // Abstract base class for all library items class LibraryItem : public IBorrowable { protected: string id; string title; bool available; int borrowedBy; public: LibraryItem(string id, string title) : id(id), title(title), available(true), borrowedBy(-1) {} bool borrow(int userId) override { if (!available) return false; available = false; borrowedBy = userId; return true; } bool returnItem() override { available = true; borrowedBy = -1; return true; } bool isAvailable() const override { return available; } string getId() const { return id; } string getTitle() const { return title; } virtual string getType() const = 0; virtual string getDetails() const = 0; virtual ~LibraryItem() = default; }; // Concrete items (Inheritance + Polymorphism) class Book : public LibraryItem { string author; string isbn; int pages; public: Book(string id, string title, string author, string isbn, int pages) : LibraryItem(id, title), author(author), isbn(isbn), pages(pages) {} string getType() const override { return "Book"; } string getDetails() const override { return title + " by " + author + " (ISBN: " + isbn + ", " + to_string(pages) + "pp)"; } string getAuthor() const { return author; } }; class DVD : public LibraryItem { string director; int durationMinutes; public: DVD(string id, string title, string director, int dur) : LibraryItem(id, title), director(director), durationMinutes(dur) {} string getType() const override { return "DVD"; } string getDetails() const override { return title + " directed by " + director + " (" + to_string(durationMinutes) + " min)"; } }; class Magazine : public LibraryItem { int issueNumber; string publisher; public: Magazine(string id, string title, string publisher, int issue) : LibraryItem(id, title), publisher(publisher), issueNumber(issue) {} string getType() const override { return "Magazine"; } string getDetails() const override { return title + " #" + to_string(issueNumber) + " by " + publisher; } }; // User types (Inheritance) class User : public INotifiable { protected: int id; string name; string email; vector<string> borrowedItems; int maxBorrows; public: User(int id, string name, string email, int maxB) : id(id), name(name), email(email), maxBorrows(maxB) {} void notify(const string& message) override { cout << "[NOTIFY " << name << "] " << message << endl; } bool canBorrow() const { return (int)borrowedItems.size() < maxBorrows; } void addBorrowed(const string& itemId) { borrowedItems.push_back(itemId); } void removeBorrowed(const string& itemId) { borrowedItems.erase( remove(borrowedItems.begin(), borrowedItems.end(), itemId), borrowedItems.end()); } int getId() const { return id; } string getName() const { return name; } virtual string getRole() const = 0; virtual ~User() = default; }; class Student : public User { public: Student(int id, string name, string email) : User(id, name, email, 3) {} string getRole() const override { return "Student"; } }; class Faculty : public User { public: Faculty(int id, string name, string email) : User(id, name, email, 10) {} string getRole() const override { return "Faculty"; } }; // ========== SERVICES (SRP + DIP) ========== // Catalog service — manages items class CatalogService : public ISearchable { map<string, unique_ptr<LibraryItem>> items; public: void addItem(unique_ptr<LibraryItem> item) { string id = item->getId(); items[id] = move(item); } LibraryItem* getItem(const string& id) { auto it = items.find(id); return it != items.end() ? it->second.get() : nullptr; } vector<string> search(const string& query) const override { vector<string> results; for (const auto& [id, item] : items) { if (item->getTitle().find(query) != string::npos || item->getDetails().find(query) != string::npos) { results.push_back(id); } } return results; } void listAll() const { for (const auto& [id, item] : items) { cout << "[" << item->getType() << "] " << item->getDetails() << (item->isAvailable() ? " ✓" : " ✗") << endl; } } }; // Borrowing service — handles borrow/return logic class BorrowService { CatalogService& catalog; map<int, User*> users; public: BorrowService(CatalogService& cat) : catalog(cat) {} void registerUser(User* user) { users[user->getId()] = user; } bool borrowItem(int userId, const string& itemId) { auto userIt = users.find(userId); if (userIt == users.end()) { cout << "User not found!" << endl; return false; } User* user = userIt->second; LibraryItem* item = catalog.getItem(itemId); if (!item) { cout << "Item not found!" << endl; return false; } if (!item->isAvailable()) { cout << "Item not available!" << endl; return false; } if (!user->canBorrow()) { cout << "Borrow limit reached!" << endl; return false; } item->borrow(userId); user->addBorrowed(itemId); user->notify("You borrowed: " + item->getTitle()); return true; } bool returnItem(int userId, const string& itemId) { auto userIt = users.find(userId); if (userIt == users.end()) return false; User* user = userIt->second; LibraryItem* item = catalog.getItem(itemId); if (!item) return false; item->returnItem(); user->removeBorrowed(itemId); user->notify("You returned: " + item->getTitle()); return true; } }; // ========== USAGE ========== int main() { // Setup CatalogService catalog; BorrowService borrowService(catalog); // Add items (Factory-like creation) catalog.addItem(make_unique<Book>("B001", "Clean Code", "Robert Martin", "978-0132350884", 464)); catalog.addItem(make_unique<Book>("B002", "Design Patterns", "GoF", "978-0201633610", 395)); catalog.addItem(make_unique<DVD>("D001", "The Matrix", "Wachowskis", 136)); catalog.addItem(make_unique<Magazine>("M001", "IEEE Spectrum", "IEEE", 42)); // Create users Student alice(1, "Alice", "alice@uni.edu"); Faculty bob(2, "Bob", "bob@uni.edu"); borrowService.registerUser(&alice); borrowService.registerUser(&bob); // Operations cout << "=== Catalog ===" << endl; catalog.listAll(); cout << "\n=== Borrowing ===" << endl; borrowService.borrowItem(1, "B001"); // Alice borrows Clean Code borrowService.borrowItem(2, "D001"); // Bob borrows The Matrix borrowService.borrowItem(1, "B001"); // Fail — already borrowed cout << "\n=== After Borrowing ===" << endl; catalog.listAll(); cout << "\n=== Search ===" << endl; auto results = catalog.search("Design"); for (const auto& id : results) { cout << "Found: " << catalog.getItem(id)->getDetails() << endl; } cout << "\n=== Return ===" << endl; borrowService.returnItem(1, "B001"); return 0; }

2. UML Class Diagrams

UML (Unified Modeling Language) is used to visualize class relationships.

BASIC NOTATION: ┌────────────────────┐ │ ClassName │ ← Class name ├────────────────────┤ - privateField │ ← Attributes │ # protectedField │ - private + publicField │ # protected ├────────────────────┤ + public + publicMethod() │ ← Methods - privateMethod()│ # protectedMethod()└────────────────────┘ RELATIONSHIPS: ──────────▷ Inheritance (IS-A) class Dog : public Animal Dog ──────────▷ Animal ─ ─ ─ ─ ─▷ Implements (Interface) class Dog : public IAnimal Dog ─ ─ ─ ─ ─▷ IAnimal ──────────► Association (uses/knows about) class Car { Driver* driver; } Car ──────────► Driver ◆────────── Composition (OWNS, part dies with whole) class Car { Engine engine; } Car ◆────────── Engine ◇────────── Aggregation (HAS, part can exist independently) class Team { vector<Player*> players; } Team ◇────────── Player - - - - - > Dependency (temporarily uses) void process(Logger& log) { log.write(...); } Processor - - - - - > Logger

Library System UML

┌──────────────┐ <<interface>> │ IBorrowable │ ├──────────────┤ +borrow()+returnItem() └──────┬───────┘ │ implements ┌──────────────┴──────────────┐ │ LibraryItem │ ├─────────────────────────────┤ │ # id: string │ │ # title: string │ │ # available: bool ├─────────────────────────────┤ + borrow(): bool+ returnItem(): bool+ getType(): string = 0 └──────┬──────────┬───────────┘ │ │ ┌────────┘ └────────┐ ┌─────┴────┐ ┌──────┐ ┌────────┴───┐ │ Book │ │ DVD │ │ Magazine │ ├──────────┤ ├──────┤ ├────────────┤ -author │ │-dir │ │ -issue │ -isbn │ │-dur │ │ -publisher │ └──────────┘ └──────┘ └────────────┘

3. Design a Parking Lot

A classic OOP design interview question.

// ========== ENUMS & TYPES ========== enum class VehicleType { MOTORCYCLE, CAR, TRUCK }; enum class SpotSize { SMALL, MEDIUM, LARGE }; // ========== VEHICLES ========== class Vehicle { protected: string licensePlate; VehicleType type; public: Vehicle(string plate, VehicleType t) : licensePlate(plate), type(t) {} string getPlate() const { return licensePlate; } VehicleType getType() const { return type; } virtual SpotSize requiredSpotSize() const = 0; virtual ~Vehicle() = default; }; class Motorcycle : public Vehicle { public: Motorcycle(string plate) : Vehicle(plate, VehicleType::MOTORCYCLE) {} SpotSize requiredSpotSize() const override { return SpotSize::SMALL; } }; class Car : public Vehicle { public: Car(string plate) : Vehicle(plate, VehicleType::CAR) {} SpotSize requiredSpotSize() const override { return SpotSize::MEDIUM; } }; class Truck : public Vehicle { public: Truck(string plate) : Vehicle(plate, VehicleType::TRUCK) {} SpotSize requiredSpotSize() const override { return SpotSize::LARGE; } }; // ========== PARKING SPOT ========== class ParkingSpot { string spotId; SpotSize size; Vehicle* parkedVehicle; public: ParkingSpot(string id, SpotSize s) : spotId(id), size(s), parkedVehicle(nullptr) {} bool canFit(const Vehicle& v) const { return !parkedVehicle && v.requiredSpotSize() <= size; } bool park(Vehicle& v) { if (!canFit(v)) return false; parkedVehicle = &v; return true; } Vehicle* unpark() { Vehicle* v = parkedVehicle; parkedVehicle = nullptr; return v; } bool isOccupied() const { return parkedVehicle != nullptr; } string getId() const { return spotId; } SpotSize getSize() const { return size; } }; // ========== PARKING LOT ========== class ParkingLot { string name; vector<unique_ptr<ParkingSpot>> spots; map<string, ParkingSpot*> vehicleToSpot; // plate → spot public: ParkingLot(string n) : name(n) {} void addSpot(string id, SpotSize size) { spots.push_back(make_unique<ParkingSpot>(id, size)); } ParkingSpot* findAvailableSpot(const Vehicle& v) { for (auto& spot : spots) { if (spot->canFit(v)) return spot.get(); } return nullptr; } bool parkVehicle(Vehicle& v) { if (vehicleToSpot.count(v.getPlate())) { cout << v.getPlate() << " already parked!" << endl; return false; } ParkingSpot* spot = findAvailableSpot(v); if (!spot) { cout << "No available spot for " << v.getPlate() << endl; return false; } spot->park(v); vehicleToSpot[v.getPlate()] = spot; cout << v.getPlate() << " parked at " << spot->getId() << endl; return true; } bool unparkVehicle(const string& plate) { auto it = vehicleToSpot.find(plate); if (it == vehicleToSpot.end()) return false; it->second->unpark(); vehicleToSpot.erase(it); cout << plate << " unparked" << endl; return true; } int availableSpots() const { return count_if(spots.begin(), spots.end(), [](const auto& s) { return !s->isOccupied(); }); } };

4. Interview Questions

Fundamentals (Q1-Q15)

Q1: What are the four pillars of OOP? Encapsulation (bundle data + methods, restrict access), Abstraction (show only essentials, hide complexity), Inheritance (derive new classes from existing ones), Polymorphism (one interface, multiple behaviors).

Q2: Class vs Object? Class = blueprint/template defining structure and behavior. Object = instance of a class occupying actual memory. Class is defined once; objects can be created many times.

Q3: What is this pointer? Implicit pointer to the current object in non-static member functions. Used for: resolving name conflicts, method chaining (return *this), passing current object to other functions.

Q4: Constructor vs Destructor? Constructor initializes objects (same name as class, no return type, can be overloaded). Destructor cleans up (prefixed with ~, no parameters, only one per class). Constructors called on creation; destructors on destruction.

Q5: What is a copy constructor? A constructor that creates an object by copying another: MyClass(const MyClass& other). Default does shallow copy. Write custom for deep copy when class manages heap memory.

Q6: Shallow vs Deep copy? Shallow: copies pointer values (both objects share same memory). Deep: allocates new memory and copies actual data. Use deep copy when class has pointer members.

Q7: What is function overloading? Multiple functions with same name but different parameter types/count. Resolved at compile time (static polymorphism). Cannot overload by return type alone.

Q8: Struct vs Class in C++? Only difference: struct defaults to public access; class defaults to private. Convention: struct for POD data, class for encapsulated objects.

Q9: What is a virtual function? A function declared with virtual keyword enabling runtime polymorphism. The correct version is called based on actual object type (not pointer type) via vtable/vptr mechanism.

Q10: What is a pure virtual function? A virtual function with = 0: no implementation in base class, must be overridden by derived classes. Makes the class abstract.

Q11: Can constructor be virtual? No. The vtable doesn't exist during construction. Use Factory Method for "virtual construction" (virtual clone/create methods).

Q12: Why make destructor virtual? Without virtual destructor, deleting derived object through base pointer only calls base destructor — derived destructor skipped, causing resource leaks.

Q13: What is the Diamond Problem? When class D inherits from B and C, both inheriting from A — D gets TWO copies of A. Causes ambiguity. Solved with virtual inheritance.

Q14: What is object slicing? Assigning derived object to base by value — derived-specific data is lost. Polymorphism lost. Fix: use pointers/references.

Q15: Static vs Dynamic binding? Static (compile-time): non-virtual functions, function overloading. Dynamic (runtime): virtual functions, resolved via vtable. Static is faster; dynamic is more flexible.

Intermediate (Q16-Q35)

Q16: What is explicit keyword? Prevents implicit type conversion via single-argument constructors. explicit Foo(int x) — can't do Foo f = 5;, must do Foo f(5);.

Q17: What is mutable keyword? Allows a member to be modified in const member functions. Use for caches, mutexes, access counters.

Q18: What is a friend function/class? Grants access to private/protected members. Not a member of the class. Commonly used for operator overloading. Does not break encapsulation — it's explicitly granted.

Q19: What are access modifiers? public: accessible everywhere. protected: accessible in class and derived classes. private: accessible only in the class. Default: private for class, public for struct.

Q20: What are static members? Belong to the class, not instances. Static data: shared across all objects, defined outside class. Static functions: no this pointer, can't access non-static members.

Q21: What is const correctness? Marking methods const when they don't modify the object. Const objects can only call const methods. Read pointer declarations right-to-left.

Q22: What is operator overloading? Defining custom behavior for operators (+, -, ==, <<) with user types. Can't overload ::, ., .*, ?:, sizeof, typeid. Can't create new operators.

Q23: What is a vtable? Compiler-generated array of function pointers for virtual functions. One per class. Each object has a vptr pointing to its class's vtable. Enables runtime dispatch.

Q24: What is RTTI? Runtime Type Information. typeid returns type info; dynamic_cast safely casts base to derived (returns nullptr if wrong type). Requires virtual functions.

Q25: dynamic_cast vs static_cast? dynamic_cast: runtime check, safe for downcasting, returns nullptr on failure. static_cast: compile-time only, unsafe for downcasting. Always prefer dynamic_cast for downcasting.

Q26: What is an abstract class? Has at least one pure virtual function. Can't be instantiated. Can have constructors, data, and implemented methods. Used as base for derived classes.

Q27: Interface vs Abstract class? Interface: ALL pure virtual, no data (pure contract). Abstract: at least one pure virtual, can have data and implementation (partial template). C++ uses abstract classes for both.

Q28: What is multiple inheritance? A class inheriting from multiple base classes. C++ supports it; Java doesn't (for classes). Risks: diamond problem, ambiguity. Mitigate with virtual inheritance.

Q29: override vs final? override: ensures function actually overrides a virtual function (compile-time check). final: prevents further overriding of a function or inheritance of a class.

Q30: What is method hiding? Non-virtual function in derived class with same name as base hides the base version. Through base pointer, base version is called. Different from overriding (virtual dispatch).

Q31: What is a functor? Class that overloads operator(). Objects act like functions. Advantages over function pointers: can hold state, inlineable, type-safe. Lambdas are compiler-generated functors.

Q32: What are covariant return types? Override can return a derived type of the base method's return type. Base::clone() returns Base*; Derived::clone() can return Derived*.

Q33: What is the Rule of 3/5/0? Rule of 3: define destructor, copy constructor, copy assignment (all or none). Rule of 5: add move constructor and move assignment. Rule of 0: use RAII types so compiler defaults work.

Q34: What is move semantics? Transfer resources from temporary objects instead of copying. Move constructor takes T&& (rvalue reference). std::move casts to rvalue. Avoids expensive copies for temporaries.

Q35: What is a delegating constructor? Constructor that calls another constructor of the same class: Foo() : Foo(0, 0) {}. Reduces code duplication.

Advanced / Design (Q36-Q50)

Q36: What are SOLID principles? S: Single Responsibility. O: Open/Closed. L: Liskov Substitution. I: Interface Segregation. D: Dependency Inversion. Five principles for maintainable, extensible OOP.

Q37: Explain SRP with example. A class should have one reason to change. Violation: Employee class that manages data, calculates pay, AND saves to database. Fix: split into Employee, PayCalculator, EmployeeRepository.

Q38: Explain OCP with example. Open for extension, closed for modification. Use interfaces/abstract classes. Adding a new notification type (Slack) shouldn't require modifying NotificationService — just add a new class implementing INotifier.

Q39: Explain LSP with example. Subtypes must substitute base types without breaking behavior. Rectangle-Square problem: Square overrides setWidth to also change height — breaks code expecting independent width/height.

Q40: Explain ISP with example. Don't force interfaces with methods clients don't use. Fat IMachine with print/scan/fax forces SimplePrinter to implement scan/fax. Fix: split into IPrinter, IScanner, IFaxer.

Q41: Explain DIP with example. Depend on abstractions, not concrete classes. Instead of UserService depending on MySQLDatabase directly, depend on IDatabase interface. Inject implementation via constructor.

Q42: What is Dependency Injection? Passing dependencies from outside instead of creating them inside the class. Constructor injection (preferred), setter injection, or method parameter. Enables testing with mocks.

Q43: Composition vs Inheritance? Composition: HAS-A (flexible, loose coupling, runtime swappable). Inheritance: IS-A (tight coupling, compile-time, polymorphism). Prefer composition. Use inheritance only for true IS-A with polymorphism.

Q44: What is the Singleton pattern? Ensures one instance with global access. Meyers' Singleton in C++: static local variable. Drawbacks: global state, hard to test, hidden dependencies.

Q45: What is Factory Method? Defines interface for creating objects; subclasses decide the type. Use when: type depends on config/environment, want to decouple creation from usage.

Q46: What is Observer pattern? One-to-many dependency: subject notifies observers on state change. Used in event systems, pub/sub, MVC. Subject maintains observer list, calls update() on changes.

Q47: What is Strategy pattern? Encapsulate algorithms in classes, make them interchangeable. Client holds interface; swaps implementations at runtime. Example: different sorting algorithms as strategy classes.

Q48: What is Decorator pattern? Add behavior dynamically by wrapping objects. Decorator implements same interface, delegates to wrapped object, adds behavior. Can stack multiple decorators.

Q49: What is RAII? Resource Acquisition Is Initialization. Resources acquired in constructor, released in destructor. C++'s mechanism for exception-safe resource management. smart pointers, lock_guard, fstream.

Q50: unique_ptr vs shared_ptr vs weak_ptr? unique_ptr: exclusive ownership, zero overhead, move-only. shared_ptr: reference-counted shared ownership, thread-safe counting. weak_ptr: non-owning observer, breaks cycles. Default to unique_ptr.


5. Coding Challenges

Challenge 1: Implement a Shape Hierarchy

Design a Shape hierarchy: - Base class Shape with pure virtual area() and perimeter() - Derived: Circle, Rectangle, Triangle - Implement operator<< for printing - Create a function that finds the shape with maximum area from a vector<Shape*>

Challenge 2: Design a Simple Observer

Implement a generic EventEmitter: - subscribe(event, callback)register a listener - unsubscribe(event, callback) — remove a listener - emit(event, data) — notify all listeners for that event - Support multiple events and multiple listeners per event

Challenge 3: Implement a Strategy-Based Compressor

Design a file compressor using Strategy pattern: - ICompressionStrategy with compress() and decompress() - Implementations: ZipStrategy, GzipStrategy, NoCompression - Compressor class that accepts any strategy - Should be swappable at runtime

Challenge 4: Build a Command Pattern Calculator

Build a calculator with undo/redo: - Commands: AddCommand, SubtractCommand, MultiplyCommand, DivideCommand - CommandHistory with undo() and redo() - Each command stores the operation and the operand - Calculator displays current value after each operation

Challenge 5: Design a Vending Machine (State Pattern)

Implement a vending machine with states: - States: Idle, HasMoney, Dispensing, SoldOut - Operations: insertMoney, selectItem, dispense, cancel - Each state handles operations differently - Use the State pattern to avoid giant if/else blocks

6. Final Checklist

Go through this before your interview: CONCEPTS: [x] Can explain all 4 pillars with examples [x] Know every constructor type and when to use each [x] Understand vtable/vptr mechanism [x] Can explain all inheritance types + diamond problem [x] Know the difference between overloading, overriding, hiding [x] Understand all SOLID principles with violation examples [x] Know at least 5 design patterns cold [x] Understand composition vs inheritance trade-offs [x] Can explain RAII and smart pointers [x] Know the Rule of 3/5/0 CODE: [x] Can implement Singleton, Factory, Observer, Strategy from memory [x] Can design a class hierarchy for any given problem [x] Can write operator overloading for common operators [x] Know when to use virtual vs non-virtual [x] Can write deep copy and move constructors [x] Understand and use const correctness DESIGN: [x] Can design a parking lot, library system, or elevator system [x] Can draw basic UML class diagrams [x] Know when to use inheritance vs composition [x] Can identify SOLID violations in code [x] Can refactor bad code to follow SOLID

🎉 Congratulations!

You've completed the entire OOP curriculum! You now understand:

  • ✅ Classes, Objects, Constructors, Destructors (from scratch)
  • ✅ Encapsulation & Abstraction (data hiding, interfaces)
  • ✅ Inheritance (all types, diamond problem, virtual inheritance)
  • ✅ Polymorphism (compile-time + runtime, vtable internals)
  • ✅ SOLID Principles (with real violations and fixes)
  • ✅ Design Patterns (Singleton, Factory, Builder, Adapter, Decorator, Proxy, Observer, Strategy, Command)
  • ✅ Advanced OOP (Composition, CRTP, RAII, Smart Pointers, DI)
  • ✅ Real-World Design (Library System, Parking Lot, UML)
  • ✅ 100+ Interview Questions with Answers

Go back to the Study Index and check off the interview readiness checklist. Good luck! 🚀