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
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 |