Oops Notes
Oops Notes
(OOP) Notes
Table of Contents
1. Introduction to OOP
2. Classes and Objects
3. Encapsulation
4. Inheritance
5. Polymorphism
6. Abstraction
7. Advanced OOP Concepts
8. Memory Management in OOP
9. Design Patterns
10. Common Interview Questions
1. Introduction to OOP
Object-Oriented Programming (OOP) is a programming paradigm that organizes code around objects
rather than functions and logic. It's based on the concept of "objects" which contain data (attributes) and
code (methods).
Real-World Analogy
Page 1 of 47
Attributes: color, model, speed, fuel level
Methods: start(), stop(), accelerate(), brake()
Encapsulation: Internal engine details are hidden
Inheritance: SportsCar inherits from Car
Polymorphism: Different cars implement start() differently
Class Definition
A class is a blueprint or template for creating objects. It defines the structure and behavior that objects
of that type will have.
class Car {
private:
string brand;
string model;
int year;
double speed;
bool isEngineOn;
public:
// Constructor
Car(string b, string m, int y) : brand(b), model(m), year(y), speed(0
// Destructor
~Car() {
cout << "Car destroyed: " << brand << " " << model << endl;
}
// Methods
void startEngine() {
isEngineOn = true;
cout << "Engine started for " << brand << " " << model << endl;
}
Page 2 of 47
}
int main() {
// Creating objects
Car car1("Toyota", "Camry", 2023);
Car car2("BMW", "X5", 2024);
// Using objects
car1.startEngine();
car1.accelerate(50);
car1.displayInfo();
return 0;
}
Key Concepts
Constructors
class Student {
private:
string name;
int age;
vector<int> grades;
Page 3 of 47
public:
// Default constructor
Student() : name("Unknown"), age(0) {}
// Parameterized constructor
Student(string n, int a) : name(n), age(a) {}
// Copy constructor
Student(const Student& other) : name(other.name), age(other.age), gra
cout << "Copy constructor called" << endl;
}
// Move constructor
Student(Student&& other) noexcept : name(move(other.name)), age(other
cout << "Move constructor called" << endl;
}
};
Destructor
class FileManager {
private:
FILE* file;
public:
FileManager(const char* filename) {
file = fopen(filename, "r");
if (!file) {
throw runtime_error("Failed to open file");
}
}
~FileManager() {
if (file) {
fclose(file);
cout << "File closed successfully" << endl;
}
}
};
Page 4 of 47
3. Encapsulation
Definition
Encapsulation is the bundling of data and methods that operate on that data within a single unit (class),
and restricting access to the internal state of the object.
Access Specifiers
class BankAccount {
private:
string accountNumber;
string holderName;
double balance;
string pin;
public:
// Constructor
BankAccount(string accNum, string name, string accountPin)
: accountNumber(accNum), holderName(name), balance(0.0), pin(acco
if (amount > 0) {
balance += amount;
Page 5 of 47
cout << "Deposited: $" << amount << ". New balance: $" << bal
return true;
}
return false;
}
Benefits of Encapsulation
4. Inheritance
Page 6 of 47
Definition
Inheritance allows a class to inherit properties and methods from another class. The class that inherits
is called the derived class (child), and the class being inherited from is called the base class (parent).
Types of Inheritance
1. Single Inheritance
// Base class
class Vehicle {
protected:
string brand;
int year;
double speed;
public:
Vehicle(string b, int y) : brand(b), year(y), speed(0) {}
// Derived class
class Car : public Vehicle {
private:
int doors;
string fuelType;
public:
Car(string b, int y, int d, string fuel)
: Vehicle(b, y), doors(d), fuelType(fuel) {}
Page 7 of 47
void displayInfo() override {
Vehicle::displayInfo();
cout << "Doors: " << doors << ", Fuel: " << fuelType << endl;
}
void openTrunk() {
cout << "Trunk opened" << endl;
}
};
2. Multiple Inheritance
class Engine {
protected:
int horsepower;
public:
Engine(int hp) : horsepower(hp) {}
void startEngine() {
cout << "Engine with " << horsepower << " HP started" << endl;
}
};
class GPS {
protected:
string currentLocation;
public:
GPS() : currentLocation("Unknown") {}
Page 8 of 47
bool autopilotEnabled;
public:
SmartCar(string b, int y, int hp)
: Vehicle(b, y), Engine(hp), GPS(), autopilotEnabled(false) {}
void enableAutopilot() {
autopilotEnabled = true;
cout << "Autopilot enabled for " << brand << endl;
}
3. Multilevel Inheritance
class Animal {
protected:
string species;
int age;
public:
Animal(string s, int a) : species(s), age(a) {}
public:
Mammal(string s, int a, bool fur) : Animal(s, a), hasFur(fur) {}
Page 9 of 47
void giveBirth() {
cout << "Mammal gives birth to live young" << endl;
}
public:
Dog(string b, int a) : Mammal("Canis lupus", a, true), breed(b) {}
void wagTail() {
cout << breed << " is wagging its tail" << endl;
}
void fetch() {
cout << "Dog is fetching the ball" << endl;
}
};
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
Page 10 of 47
class PublicDerived : public Base {
// publicVar remains public
// protectedVar remains protected
// privateVar is not accessible
};
// Protected inheritance
class ProtectedDerived : protected Base {
// publicVar becomes protected
// protectedVar remains protected
// privateVar is not accessible
};
// Private inheritance
class PrivateDerived : private Base {
// publicVar becomes private
// protectedVar becomes private
// privateVar is not accessible
};
5. Polymorphism
Definition
Polymorphism allows objects of different types to be treated as objects of a common base type. The
word "polymorphism" comes from Greek, meaning "many forms."
Types of Polymorphism
Function Overloading
class Calculator {
public:
// Overloaded add functions
int add(int a, int b) {
return a + b;
}
Page 11 of 47
return a + b;
}
Operator Overloading
class Complex {
private:
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// Overload + operator
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// Overload == operator
bool operator==(const Complex& other) const {
return (real == other.real) && (imag == other.imag);
}
// Overload [] operator
double& operator[](int index) {
if (index == 0) return real;
else if (index == 1) return imag;
throw out_of_range("Index out of range");
Page 12 of 47
}
};
Virtual Functions
class Shape {
protected:
string color;
public:
Shape(string c) : color(c) {}
public:
Circle(string c, double r) : Shape(c), radius(r) {}
Page 13 of 47
}
};
public:
Rectangle(string c, double w, double h) : Shape(c), width(w), height(
// Real-world usage
void processShape(const Shape& shape) {
shape.display(); // Calls appropriate derived class method
cout << "Processing complete\n" << endl;
}
int main() {
Circle circle("Red", 5.0);
Rectangle rectangle("Blue", 4.0, 6.0);
Page 14 of 47
shape->display();
}
return 0;
}
// Virtual destructor
virtual ~Database() = default;
public:
MySQLDatabase(const string& connStr) : connectionString(connStr), isC
Page 15 of 47
logActivity("Disconnected from MySQL");
}
public:
PostgreSQLDatabase(string h, string p, string db)
: host(h), port(p), database(db), isConnected(false) {}
Page 16 of 47
6. Abstraction
Definition
Abstraction is the process of hiding complex implementation details while showing only the essential
features of an object. It focuses on what an object does rather than how it does it.
Page 17 of 47
public:
// Simple interface for users
bool processPayment(double amount, const string& method) override {
cout << "Processing " << method << " payment of $" << amount << e
if (communicateWithBank(amount)) {
updateAccountBalance(amount);
return true;
}
return false;
}
public:
ATM() : processor(make_unique<ATMProcessor>()), currentBalance(1000.0
Page 18 of 47
cout << "Please take your cash: $" << amount << endl;
}
} else {
cout << "Insufficient funds!" << endl;
}
} else {
cout << "Invalid credentials!" << endl;
}
}
public:
BaseMediaPlayer() : isPlaying(false), isPaused(false), currentVolume(
Page 19 of 47
void pause() override {
if (isPlaying) {
isPaused = !isPaused;
cout << (isPaused ? "Paused" : "Resumed") << " playback" << e
}
}
// Concrete implementations
class MP3Player : public BaseMediaPlayer {
public:
void play(const string& filename) override {
if (filename.substr(filename.length() - 4) != ".mp3") {
cout << "Error: MP3Player can only play .mp3 files" << endl;
return;
}
currentFile = filename;
isPlaying = true;
isPaused = false;
cout << "Playing MP3: " << filename << " at " << currentVolume <<
}
};
Page 20 of 47
currentFile = filename;
isPlaying = true;
isPaused = false;
cout << "Playing video: " << filename << " with audio at " << cur
}
};
class BankManager {
public:
// Friend function can access private members of BankAccount
void auditAccount(const BankAccount& account);
double calculateInterest(const BankAccount& account, double rate);
};
class BankAccount {
private:
string accountNumber;
double balance;
vector<string> transactionHistory;
public:
BankAccount(string accNum, double initialBalance)
: accountNumber(accNum), balance(initialBalance) {}
Page 21 of 47
bool withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
transactionHistory.push_back("Withdrew: $" + to_string(amount
return true;
}
return false;
}
// Friend class
class AccountingSystem {
public:
void generateReport(const BankAccount& account) {
// Can access private members directly
cout << "Account Report for " << account.accountNumber << endl;
cout << "Current Balance: $" << account.balance << endl;
cout << "Total Transactions: " << account.transactionHistory.size
}
};
Static Members
class Employee {
private:
Page 22 of 47
static int totalEmployees; // Static data member
static double totalSalary; // Shared among all instances
int employeeId;
string name;
double salary;
public:
Employee(string n, double s) : name(n), salary(s) {
employeeId = ++totalEmployees;
totalSalary += salary;
cout << "Employee " << name << " hired. Total employees: " << tot
}
~Employee() {
totalEmployees--;
totalSalary -= salary;
cout << "Employee " << name << " left. Total employees: " << tota
}
Page 23 of 47
// Static member definitions (required outside class)
int Employee::totalEmployees = 0;
double Employee::totalSalary = 0.0;
Composition vs Aggregation
public:
Engine(int cyl, double disp) : cylinders(cyl), displacement(disp) {}
void start() {
cout << cylinders << "-cylinder engine started" << endl;
}
class Car {
private:
Engine engine; // Composition - Engine is part of Car
string brand;
string model;
public:
// Engine is created when Car is created
Car(string b, string m, int cylinders, double displacement)
: engine(cylinders, displacement), brand(b), model(m) {}
void start() {
cout << "Starting " << brand << " " << model << endl;
engine.start(); // Using composed object
}
void displaySpecs() {
cout << brand << " " << model << " - " << engine.getDisplacement(
}
Page 24 of 47
};
public:
Driver(string n, string license) : name(n), licenseNumber(license) {}
void drive() {
cout << "Driver " << name << " is driving" << endl;
}
class Taxi {
private:
string plateNumber;
Driver* currentDriver; // Aggregation - Driver exists independentl
public:
Taxi(string plate) : plateNumber(plate), currentDriver(nullptr) {}
void removeDriver() {
if (currentDriver) {
cout << "Driver " << currentDriver->getName() << " removed fr
currentDriver = nullptr; // Driver still exists elsewhere
}
}
void startRide() {
if (currentDriver) {
cout << "Taxi " << plateNumber << " starting ride" << endl;
currentDriver->drive();
} else {
cout << "No driver assigned to taxi " << plateNumber << endl;
Page 25 of 47
}
}
};
Smart Pointers
#include <memory>
class Resource {
private:
string name;
vector<int> data;
public:
Resource(string n) : name(n), data(1000, 42) {
cout << "Resource '" << name << "' created" << endl;
}
~Resource() {
cout << "Resource '" << name << "' destroyed" << endl;
}
void process() {
cout << "Processing resource: " << name << endl;
}
// Transfer ownership
Page 26 of 47
unique_ptr<Resource> resource2 = move(resource1);
// resource1 is now nullptr
if (resource2) {
resource2->process();
}
} // resource2 automatically deleted here
{
shared_ptr<Resource> shared2 = shared1; // Copy, increases r
cout << "Reference count: " << shared1.use_count() << endl;
shared2->process();
} // shared2 goes out of scope, ref count decreases
Page 27 of 47
public:
FileHandler(const string& fname)
: file(nullptr, &fclose), filename(fname) {
~FileHandler() {
cout << "File closed: " << filename << endl;
}
class LargeObject {
private:
string name;
vector<double> data;
public:
// Constructor
LargeObject(const string& n, size_t size)
: name(n), data(size, 3.14) {
cout << "Constructor: " << name << " (size: " << size << ")" << e
}
// Copy constructor
LargeObject(const LargeObject& other)
: name(other.name + "_copy"), data(other.data) {
Page 28 of 47
cout << "Copy constructor: " << name << " (expensive deep copy)"
}
// Move constructor
LargeObject(LargeObject&& other) noexcept
: name(move(other.name)), data(move(other.data)) {
cout << "Move constructor: " << name << " (efficient move)" << en
other.name = "moved_from";
}
// Destructor
~LargeObject() {
cout << "Destructor: " << name << endl;
}
void demonstrateMove() {
cout << "\n=== Move Semantics Demo ===" << endl;
Page 29 of 47
LargeObject obj1("Original", 1000000);
// Copy - expensive
LargeObject obj2 = obj1;
// Move - efficient
LargeObject obj3 = move(obj1); // obj1 is now in moved-from state
obj2.display();
obj3.display();
9. Design Patterns
Singleton Pattern
class Logger {
private:
static unique_ptr<Logger> instance;
static mutex mtx;
string logFile;
ofstream fileStream;
// Private constructor
Logger(const string& filename) : logFile(filename) {
fileStream.open(logFile, ios::app);
if (!fileStream.is_open()) {
throw runtime_error("Cannot open log file: " + filename);
}
}
public:
// Delete copy constructor and assignment operator
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
Page 30 of 47
static Logger* getInstance(const string& filename = "app.log") {
lock_guard<mutex> lock(mtx);
if (instance == nullptr) {
instance = unique_ptr<Logger>(new Logger(filename));
}
return instance.get();
}
cout << "[" << level << "] " << message << endl;
}
~Logger() {
if (fileStream.is_open()) {
fileStream.close();
}
}
};
Factory Pattern
// Abstract product
class Vehicle {
public:
virtual void start() = 0;
Page 31 of 47
virtual void stop() = 0;
virtual double getPrice() const = 0;
virtual string getType() const = 0;
virtual ~Vehicle() = default;
};
// Concrete products
class Car : public Vehicle {
private:
string model;
double price;
public:
Car(const string& m, double p) : model(m), price(p) {}
public:
Motorcycle(const string& b, double p) : brand(b), price(p) {}
Page 32 of 47
string getType() const override { return "Motorcycle: " + brand; }
};
public:
Truck(const string& m, double p, int capacity)
: model(m), price(p), loadCapacity(capacity) {}
// Factory class
class VehicleFactory {
public:
enum class VehicleType {
CAR,
MOTORCYCLE,
TRUCK
};
Page 33 of 47
case VehicleType::MOTORCYCLE:
return make_unique<Motorcycle>(model, price);
case VehicleType::TRUCK:
return make_unique<Truck>(model, price, capacity);
default:
throw invalid_argument("Unknown vehicle type");
}
}
// Usage example
void demonstrateFactory() {
cout << "\n=== Factory Pattern Demo ===" << endl;
vector<unique_ptr<Vehicle>> vehicles;
Page 34 of 47
}
}
Observer Pattern
// Observer interface
class Observer {
public:
virtual void update(const string& message) = 0;
virtual string getName() const = 0;
virtual ~Observer() = default;
};
// Subject interface
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify(const string& message) = 0;
virtual ~Subject() = default;
};
public:
void attach(Observer* observer) override {
observers.push_back(observer);
cout << observer->getName() << " subscribed to news updates" << e
}
Page 35 of 47
void notify(const string& message) override {
cout << "\nNotifying all subscribers about: " << message << endl;
for (Observer* observer : observers) {
observer->update(message);
}
}
// Concrete observers
class NewsChannel : public Observer {
private:
string channelName;
vector<string> newsHistory;
public:
NewsChannel(const string& name) : channelName(name) {}
Page 36 of 47
public:
OnlinePortal(const string& name) : portalName(name) {}
private:
void sendPushNotification(const string& news) {
cout << "[" << portalName << "] Push notification sent to mobile
}
};
// Usage demonstration
void demonstrateObserver() {
cout << "\n=== Observer Pattern Demo ===" << endl;
NewsAgency agency;
NewsChannel cnn("CNN");
NewsChannel bbc("BBC");
OnlinePortal yahoo("Yahoo News");
OnlinePortal google("Google News");
// Subscribe observers
agency.attach(&cnn);
agency.attach(&bbc);
agency.attach(&yahoo);
agency.attach(&google);
// Publish news
agency.publishNews("Breaking: New technology breakthrough announced!"
agency.publishNews("Sports: Championship finals this weekend");
// Show history
Page 37 of 47
cnn.showHistory();
}
Answer:
class BankAccount {
private:
double balance; // Hidden from outside access
public:
void deposit(double amount) { // Controlled access
if (amount > 0) balance += amount;
}
double getBalance() const { return balance; }
};
class Animal {
public:
virtual void makeSound() = 0;
};
Page 38 of 47
class DatabaseManager {
public:
void saveData(const string& data); // Simple interface
private:
void establishConnection(); // Complex details hidden
void executeQuery(const string& query);
void handleErrors();
};
Answer:
class Calculator {
public:
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }
};
class Animal {
public:
virtual void speak() { cout << "Animal sound" << endl; }
};
Page 39 of 47
void speak() override { cout << "Woof!" << endl; }
};
Answer:
Virtual Functions:
Enable runtime polymorphism. The actual function called depends on the object type, not pointer type.
class Shape {
public:
virtual double area() const = 0; // Pure virtual
virtual void display() const { // Virtual with default implementat
cout << "This is a shape" << endl;
}
};
// Usage
Shape* shape = new Circle(5.0);
shape->display(); // Calls Circle::display(), not Shape::display()
delete shape;
Virtual Destructors:
Ensure proper cleanup when deleting objects through base class pointers.
class Base {
public:
virtual ~Base() { // Virtual destructor
cout << "Base destructor" << endl;
}
};
Page 40 of 47
class Derived : public Base {
private:
int* data;
public:
Derived() : data(new int[100]) {}
~Derived() override {
delete[] data; // This will be called even when deleting through
cout << "Derived destructor" << endl;
}
};
Answer:
class Animal {
protected:
string name;
public:
virtual void eat() { cout << name << " is eating" << endl; }
};
class Engine {
public:
void start() { cout << "Engine started" << endl; }
};
Page 41 of 47
class Car {
private:
Engine engine; // Car HAS-A Engine
string brand;
public:
Car(string b) : brand(b) {}
void start() {
cout << "Starting " << brand << endl;
engine.start(); // Delegating to composed object
}
};
When to use:
Inheritance: When you have a clear "is-a" relationship and want to extend behavior
Composition: When you want to use functionality of another class without inheritance hierarchy
Answer:
class MyClass {
private:
int* data;
size_t size;
public:
// Constructor
MyClass(size_t s) : size(s), data(new int[s]) {
cout << "Constructor called" << endl;
}
Page 42 of 47
size = other.size;
data = new int[size];
copy(other.data, other.data + size, data);
}
return *this;
}
// Destructor
~MyClass() {
delete[] data;
cout << "Destructor called" << endl;
}
};
// Usage examples
MyClass obj1(10); // Constructor
MyClass obj2 = obj1; // Copy constructor (not assignment!)
MyClass obj3(5); // Constructor
obj3 = obj1; // Assignment operator
Answer:
Abstract Class:
public:
Animal(string s) : species(s) {}
Page 43 of 47
virtual void sleep() const {
cout << species << " is sleeping" << endl;
}
// Non-virtual function
void breathe() const {
cout << species << " is breathing" << endl;
}
};
public:
Circle(double r) : radius(r) {}
Page 44 of 47
void resize(double scale) override {
radius *= scale;
}
Answer:
The diamond problem occurs in multiple inheritance when a class inherits from two classes that have a
common base class.
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
virtual void eat() { cout << name << " is eating" << endl; }
};
Page 45 of 47
Solution - Virtual Inheritance:
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {
cout << "Animal constructor: " << name << endl;
}
virtual void eat() { cout << name << " is eating" << endl; }
};
void fly() { cout << name << " is flying" << endl; }
};
// Usage
Bat bat("Batman");
bat.eat(); // No ambiguity - only one Animal base
bat.giveBirth(); // From Mammal
bat.layEggs(); // From Bird
bat.fly(); // From Bat
Problem 1: Design a library management system with books, members, and borrowing functionality.
Page 46 of 47
Problem 2: Implement a shape hierarchy with different types of shapes and area calculations.
Problem 3: Create a vehicle rental system with different vehicle types and pricing strategies.
Problem 4: Design a notification system that can send notifications through different channels (email,
SMS, push notifications).
Problem 5: Implement a simple banking system with different account types and transaction history.
Summary
Object-Oriented Programming in C++ provides powerful tools for creating maintainable, scalable, and
reusable code. The key concepts covered in these notes include:
Understanding these concepts and their practical applications will help you write better C++ code and
succeed in technical interviews. Practice implementing these examples and try to identify OOP patterns
in real-world applications.
Key Takeaways:
Remember: Good OOP design is about modeling real-world relationships and creating code that is easy
to understand, maintain, and extend.
Page 47 of 47