Skip to content

C++ and Python Toolkit

TL;DR: Every Java OOP feature used in LLD has a direct equivalent in C++ and Python. This lesson gives you the translation table, reusable boilerplate for both languages, and the pitfalls that trip up candidates who aren't using Java.

The Translation Table

This is the reference you'll come back to. Every row is a Java feature you'll use in LLD interviews, with the exact C++ and Python equivalent.

Java Feature C++ Equivalent Python Equivalent
interface Class with pure virtual functions (= 0) ABC with @abstractmethod or Protocol
abstract class Class with at least one pure virtual function ABC with mix of abstract and concrete methods
implements / extends : public Base (inheritance) class Child(Base):
Multiple interfaces Multiple inheritance from abstract classes Multiple inheritance from ABCs
private field private: section _name convention (no enforcement)
protected field protected: section _name convention (same as private)
public method public: section Default (no prefix)
enum with behavior enum class + separate map/function Enum with methods and __init__
Generics (<T>) Templates (template<typename T>) Type hints (list[T], Generic[T])
final class final keyword (C++11) No built-in equivalent
@Override override keyword (C++11) No keyword; just redefine the method
Collections.unmodifiableList() Return const reference Return tuple() or frozenset()
HashMap std::unordered_map dict
TreeMap std::map (ordered by default) dict (insertion-ordered, but no sorted iteration -- use sortedcontainers.SortedDict)
ArrayList std::vector list
HashSet std::unordered_set set
Queue / PriorityQueue std::queue / std::priority_queue collections.deque / heapq

C++ Equivalents in Detail

Interfaces via Pure Virtual Functions

C++ doesn't have an interface keyword. Instead, you create a class where every method is pure virtual:

// This IS an interface -- all methods are pure virtual
class PaymentProcessor {
public:
    virtual bool charge(Money amount, PaymentMethod method) = 0;
    virtual bool refund(const std::string& transactionId) = 0;
    virtual ~PaymentProcessor() = default;  // Always add virtual destructor
};

class StripeProcessor : public PaymentProcessor {
public:
    bool charge(Money amount, PaymentMethod method) override {
        // Stripe-specific logic
        return true;
    }

    bool refund(const std::string& transactionId) override {
        // Stripe-specific logic
        return true;
    }
};

The = 0 makes a method pure virtual, meaning any subclass must implement it. The override keyword (C++11) tells the compiler to verify you're actually overriding a virtual method -- use it every time.

Critical: Always declare a virtual destructor in your base class. Without it, deleting a derived object through a base pointer causes undefined behavior.

Abstract Classes

Mix pure virtual methods (subclass must implement) with concrete methods (shared behavior):

class Vehicle {
private:
    std::string licensePlate;
    VehicleType type;

public:
    Vehicle(std::string plate, VehicleType type)
        : licensePlate(std::move(plate)), type(type) {}

    virtual ~Vehicle() = default;

    std::string getLicensePlate() const { return licensePlate; }
    VehicleType getType() const { return type; }

    // Subclasses must implement
    virtual int getRequiredSpots() const = 0;
};

Enum Class

C++ enum class provides type safety but no built-in behavior like Java enums. You pair it with a function or map:

enum class VehicleType {
    MOTORCYCLE,
    CAR,
    BUS
};

// Option 1: Function
int getRequiredSpots(VehicleType type) {
    switch (type) {
        case VehicleType::MOTORCYCLE: return 1;
        case VehicleType::CAR:        return 2;
        case VehicleType::BUS:        return 5;
    }
    throw std::invalid_argument("Unknown vehicle type");
}

// Option 2: Static map
const std::unordered_map<VehicleType, int> SPOT_REQUIREMENTS = {
    {VehicleType::MOTORCYCLE, 1},
    {VehicleType::CAR, 2},
    {VehicleType::BUS, 5}
};

Neither option forces you to handle new types at compile time the way Java enums do. This is a real trade-off. In an interview, mention it to show awareness.

Smart Pointers for Ownership

In Java, garbage collection handles memory. In C++, you need smart pointers:

// Unique ownership -- one owner, automatically deleted
std::unique_ptr<Vehicle> vehicle = std::make_unique<Car>("ABC-123");

// Shared ownership -- reference counted
std::shared_ptr<PaymentProcessor> processor = std::make_shared<StripeProcessor>();

// Never use raw new/delete in LLD interviews

In an interview, default to std::unique_ptr unless multiple objects need to share ownership.

Python Equivalents in Detail

Interfaces via ABC and Protocol

Python gives you two approaches. Use ABC when you want enforced contracts:

from abc import ABC, abstractmethod

class PaymentProcessor(ABC):
    @abstractmethod
    def charge(self, amount: Money, method: PaymentMethod) -> bool:
        pass

    @abstractmethod
    def refund(self, transaction_id: str) -> bool:
        pass

class StripeProcessor(PaymentProcessor):
    def charge(self, amount: Money, method: PaymentMethod) -> bool:
        # Stripe-specific logic
        return True

    def refund(self, transaction_id: str) -> bool:
        # Stripe-specific logic
        return True

If you forget to implement refund(), Python raises TypeError when you try to instantiate StripeProcessor. The check happens at instantiation time, not at class definition time -- a key difference from Java.

Protocol (from typing) provides structural typing -- "if it has these methods, it qualifies":

from typing import Protocol

class PaymentProcessor(Protocol):
    def charge(self, amount: Money, method: PaymentMethod) -> bool: ...
    def refund(self, transaction_id: str) -> bool: ...

# StripeProcessor doesn't need to inherit from PaymentProcessor.
# It just needs to have the right methods.
class StripeProcessor:
    def charge(self, amount: Money, method: PaymentMethod) -> bool:
        return True

    def refund(self, transaction_id: str) -> bool:
        return True

For interviews, ABC is clearer and more explicit. Use Protocol when you want duck typing with type checker support.

Enums with Behavior

Python enums can have methods and custom __init__:

from enum import Enum

class VehicleType(Enum):
    MOTORCYCLE = 1
    CAR = 2
    BUS = 5

    def __init__(self, required_spots: int):
        self._required_spots = required_spots

    @property
    def required_spots(self) -> int:
        return self._required_spots

# Usage
spots = VehicleType.CAR.required_spots  # 2

This is closer to Java's enum-with-fields pattern than most candidates realize. Use it in interviews to show sophistication.

Dataclasses for Value Objects

When a class is mostly data with minimal behavior, dataclass eliminates boilerplate:

from dataclasses import dataclass

@dataclass(frozen=True)  # frozen=True makes it immutable
class Money:
    amount_cents: int
    currency: str = "USD"

    def add(self, other: "Money") -> "Money":
        if self.currency != other.currency:
            raise ValueError("Cannot add different currencies")
        return Money(self.amount_cents + other.amount_cents, self.currency)

frozen=True makes the object immutable and hashable -- useful for value objects that serve as map keys.

The Privacy Convention

Python has no access enforcement. It relies on convention:

class BankAccount:
    def __init__(self, balance_cents: int):
        self._balance_cents = balance_cents   # "private" by convention
        self.__internal = "name-mangled"       # Harder to access, but not truly private

    @property
    def balance_cents(self) -> int:
        return self._balance_cents

Single underscore _name means "don't touch this from outside." Double underscore __name triggers name mangling (_ClassName__name), making accidental access harder but not impossible.

In interviews, use single underscore and @property. Mention that Python trusts developers to respect conventions rather than enforcing access.

Reusable C++ LLD Boilerplate

Copy this as your starting template for any C++ LLD interview:

#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <memory>
#include <stdexcept>
#include <queue>

// --- Enums ---
enum class SpotType { MOTORCYCLE, COMPACT, LARGE };

// --- Interfaces (pure virtual) ---
class Parkable {
public:
    virtual int getRequiredSpots() const = 0;
    virtual SpotType getMinSpotType() const = 0;
    virtual ~Parkable() = default;
};

// --- Abstract base class ---
class Vehicle : public Parkable {
private:
    std::string licensePlate;

public:
    explicit Vehicle(std::string plate) : licensePlate(std::move(plate)) {}
    std::string getLicensePlate() const { return licensePlate; }
    ~Vehicle() override = default;
};

// --- Concrete class ---
class Car : public Vehicle {
public:
    explicit Car(std::string plate) : Vehicle(std::move(plate)) {}
    int getRequiredSpots() const override { return 1; }
    SpotType getMinSpotType() const override { return SpotType::COMPACT; }
};

// --- Manager class with ownership ---
class ParkingLot {
private:
    std::unordered_map<std::string, std::unique_ptr<Vehicle>> parkedVehicles;

public:
    void parkVehicle(std::unique_ptr<Vehicle> vehicle) {
        std::string plate = vehicle->getLicensePlate();
        parkedVehicles[plate] = std::move(vehicle);
    }

    // Return raw pointer for read access, unique_ptr retains ownership
    const Vehicle* getVehicle(const std::string& plate) const {
        auto it = parkedVehicles.find(plate);
        return it != parkedVehicles.end() ? it->second.get() : nullptr;
    }
};

Reusable Python LLD Boilerplate

Copy this as your starting template for any Python LLD interview:

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional


# --- Enums ---
class SpotType(Enum):
    MOTORCYCLE = 1
    COMPACT = 2
    LARGE = 3


# --- Interfaces (ABC) ---
class Parkable(ABC):
    @abstractmethod
    def get_required_spots(self) -> int:
        pass

    @abstractmethod
    def get_min_spot_type(self) -> SpotType:
        pass


# --- Abstract base class ---
class Vehicle(Parkable):
    def __init__(self, license_plate: str):
        self._license_plate = license_plate

    @property
    def license_plate(self) -> str:
        return self._license_plate


# --- Concrete class ---
class Car(Vehicle):
    def get_required_spots(self) -> int:
        return 1

    def get_min_spot_type(self) -> SpotType:
        return SpotType.COMPACT


# --- Value object ---
@dataclass(frozen=True)
class ParkingTicket:
    ticket_id: str
    license_plate: str
    spot_id: str


# --- Manager class ---
class ParkingLot:
    def __init__(self):
        self._parked_vehicles: dict[str, Vehicle] = {}

    def park_vehicle(self, vehicle: Vehicle) -> None:
        self._parked_vehicles[vehicle.license_plate] = vehicle

    def get_vehicle(self, plate: str) -> Optional[Vehicle]:
        return self._parked_vehicles.get(plate)

Common Pitfalls by Language

Python: No True Private

account = BankAccount(1000)
account._balance_cents = -9999  # Python won't stop you

In an interview, acknowledge this. Say: "Python uses conventions instead of enforcement. The underscore signals intent, but nothing prevents external access. For this design, we rely on the team respecting the interface."

C++: Multiple Inheritance Complexity

Java allows single class inheritance plus multiple interfaces. C++ allows multiple inheritance from any classes, which introduces the diamond problem:

class A { public: void doSomething(); };
class B : public A {};
class C : public A {};
class D : public B, public C {};  // Two copies of A -- ambiguous!

The fix is virtual inheritance, but it adds complexity. In interviews, stick to single inheritance from one concrete class and multiple inheritance only from pure abstract classes (interfaces). This mirrors the Java model and avoids the diamond problem entirely.

Python: ABC Checks Happen Late

class Broken(PaymentProcessor):
    pass  # Forgot to implement charge() and refund()

# This line is fine -- no error yet
x = None

# Error only happens HERE, at instantiation
x = Broken()  # TypeError: Can't instantiate abstract class

Java and C++ catch this at compile time. In Python, you won't know until you actually create an instance. In interviews, mention that you'd rely on type checkers like mypy to catch this earlier.

Quick Recap

Concern Java C++ Python
Define an interface interface keyword Pure virtual class (= 0) ABC + @abstractmethod or Protocol
Enforce implementation Compile-time error Compile-time error Runtime TypeError at instantiation
Access control private/protected/public Same three keywords + sections Convention only (_prefix)
Enums with data Enum with fields + constructor enum class + external map/function Enum with __init__ and properties
Memory management Garbage collected Smart pointers (unique_ptr, shared_ptr) Garbage collected
Generics <T> with type erasure template<typename T> (compile-time) Type hints (not enforced at runtime)
Collections HashMap, ArrayList, HashSet unordered_map, vector, unordered_set dict, list, set