0% found this document useful (0 votes)
56 views47 pages

Oops Notes

This document provides comprehensive notes on C++ Object-Oriented Programming (OOP), covering key concepts such as classes, objects, encapsulation, inheritance, polymorphism, and abstraction. It includes detailed explanations, code examples, and real-world analogies to illustrate the principles of OOP. Additionally, it addresses advanced topics like memory management, design patterns, and common interview questions related to OOP.

Uploaded by

rootgoghph
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
56 views47 pages

Oops Notes

This document provides comprehensive notes on C++ Object-Oriented Programming (OOP), covering key concepts such as classes, objects, encapsulation, inheritance, polymorphism, and abstraction. It includes detailed explanations, code examples, and real-world analogies to illustrate the principles of OOP. Additionally, it addresses advanced topics like memory management, design patterns, and common interview questions related to OOP.

Uploaded by

rootgoghph
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 47

Complete C++ Object-Oriented Programming

(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

What is Object-Oriented Programming?

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

Key Principles of OOP

1. Encapsulation - Bundling data and methods together


2. Inheritance - Creating new classes based on existing ones
3. Polymorphism - Using one interface for different underlying forms
4. Abstraction - Hiding complex implementation details

Real-World Analogy

Think of a Car as an object:

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

2. Classes and Objects

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

void accelerate(double increment) {


if (isEngineOn) {
speed += increment;
cout << "Speed increased to " << speed << " km/h" << endl;
}

Page 2 of 47
}

void displayInfo() const {


cout << year << " " << brand << " " << model <<
" - Speed: " << speed << " km/h" << endl;
}

// Getters and Setters


string getBrand() const { return brand; }
double getSpeed() const { return speed; }
};

Object Creation and Usage

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

Default Constructor: Takes no parameters


Parameterized Constructor: Takes parameters to initialize object
Copy Constructor: Creates object as copy of another object
Move Constructor: Transfers resources from temporary object

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

Automatically called when object goes out of scope or is explicitly deleted.

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

private: Accessible only within the same class


protected: Accessible within the class and its derived classes
public: Accessible from anywhere

Real-World Example: Bank Account

class BankAccount {
private:
string accountNumber;
string holderName;
double balance;
string pin;

// Private helper method


bool validatePin(const string& inputPin) const {
return pin == inputPin;
}

public:
// Constructor
BankAccount(string accNum, string name, string accountPin)
: accountNumber(accNum), holderName(name), balance(0.0), pin(acco

// Public interface methods


bool deposit(double amount, const string& inputPin) {
if (!validatePin(inputPin)) {
cout << "Invalid PIN!" << endl;
return false;
}

if (amount > 0) {
balance += amount;

Page 5 of 47
cout << "Deposited: $" << amount << ". New balance: $" << bal
return true;
}
return false;
}

bool withdraw(double amount, const string& inputPin) {


if (!validatePin(inputPin)) {
cout << "Invalid PIN!" << endl;
return false;
}

if (amount > 0 && amount <= balance) {


balance -= amount;
cout << "Withdrawn: $" << amount << ". New balance: $" << bal
return true;
}
cout << "Insufficient funds or invalid amount!" << endl;
return false;
}

double getBalance(const string& inputPin) const {


if (validatePin(inputPin)) {
return balance;
}
cout << "Invalid PIN!" << endl;
return -1;
}

string getAccountNumber() const { return accountNumber; }


string getHolderName() const { return holderName; }
};

Benefits of Encapsulation

1. Data Security: Sensitive data is protected from unauthorized access


2. Data Integrity: Data can only be modified through controlled methods
3. Modularity: Internal implementation can be changed without affecting external code
4. Maintenance: Easier to maintain and debug code

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) {}

virtual void start() {


cout << "Vehicle started" << endl;
}

virtual void displayInfo() {


cout << year << " " << brand << " - Speed: " << speed << " km/h"
}

virtual ~Vehicle() = default;


};

// 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) {}

void start() override {


cout << "Car engine started with " << fuelType << endl;
}

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") {}

void navigate(const string& destination) {


cout << "Navigating from " << currentLocation << " to " << destin
}

void setLocation(const string& location) {


currentLocation = location;
}
};

class SmartCar : public Vehicle, public Engine, public GPS {


private:

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

void start() override {


Vehicle::start();
startEngine();
cout << "Smart car systems initialized" << endl;
}
};

3. Multilevel Inheritance

class Animal {
protected:
string species;
int age;

public:
Animal(string s, int a) : species(s), age(a) {}

virtual void makeSound() {


cout << "Animal makes a sound" << endl;
}

virtual void eat() {


cout << "Animal is eating" << endl;
}
};

class Mammal : public Animal {


protected:
bool hasFur;

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

void makeSound() override {


cout << "Mammal makes a sound" << endl;
}
};

class Dog : public Mammal {


private:
string breed;

public:
Dog(string b, int a) : Mammal("Canis lupus", a, true), breed(b) {}

void makeSound() override {


cout << "Dog barks: Woof! Woof!" << endl;
}

void wagTail() {
cout << breed << " is wagging its tail" << endl;
}

void fetch() {
cout << "Dog is fetching the ball" << endl;
}
};

Access Specifiers in Inheritance

class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};

// Public inheritance - most common

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

1. Compile-Time Polymorphism (Static)

Function Overloading

class Calculator {
public:
// Overloaded add functions
int add(int a, int b) {
return a + b;
}

double add(double a, double b) {

Page 11 of 47
return a + b;
}

int add(int a, int b, int c) {


return a + b + c;
}

string add(const string& a, const string& b) {


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


friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}

// 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
}
};

2. Runtime Polymorphism (Dynamic)

Virtual Functions

class Shape {
protected:
string color;

public:
Shape(string c) : color(c) {}

// Virtual function - enables runtime polymorphism


virtual double calculateArea() const = 0; // Pure virtual function
virtual double calculatePerimeter() const = 0;

virtual void display() const {


cout << "Shape with color: " << color << endl;
}

virtual ~Shape() = default; // Virtual destructor


};

class Circle : public Shape {


private:
double radius;

public:
Circle(string c, double r) : Shape(c), radius(r) {}

double calculateArea() const override {


return 3.14159 * radius * radius;
}

double calculatePerimeter() const override {


return 2 * 3.14159 * radius;
}

void display() const override {


cout << "Circle with radius " << radius << " and color " << color
cout << "Area: " << calculateArea() << ", Perimeter: " << calcula

Page 13 of 47
}
};

class Rectangle : public Shape {


private:
double width, height;

public:
Rectangle(string c, double w, double h) : Shape(c), width(w), height(

double calculateArea() const override {


return width * height;
}

double calculatePerimeter() const override {


return 2 * (width + height);
}

void display() const override {


cout << "Rectangle " << width << "x" << height << " with color "
cout << "Area: " << calculateArea() << ", Perimeter: " << calcula
}
};

// 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);

// Runtime polymorphism in action


processShape(circle); // Calls Circle::display()
processShape(rectangle); // Calls Rectangle::display()

// Using polymorphism with pointers


vector<unique_ptr<Shape>> shapes;
shapes.push_back(make_unique<Circle>("Green", 3.0));
shapes.push_back(make_unique<Rectangle>("Yellow", 5.0, 8.0));

for (const auto& shape : shapes) {

Page 14 of 47
shape->display();
}

return 0;
}

Abstract Classes and Pure Virtual Functions

// Abstract base class


class Database {
public:
// Pure virtual functions make this an abstract class
virtual void connect() = 0;
virtual void disconnect() = 0;
virtual bool executeQuery(const string& query) = 0;

// Virtual destructor
virtual ~Database() = default;

// Non-pure virtual function (can be overridden)


virtual void logActivity(const string& activity) {
cout << "Database activity: " << activity << endl;
}
};

class MySQLDatabase : public Database {


private:
string connectionString;
bool isConnected;

public:
MySQLDatabase(const string& connStr) : connectionString(connStr), isC

void connect() override {


cout << "Connecting to MySQL database..." << endl;
isConnected = true;
logActivity("Connected to MySQL");
}

void disconnect() override {


cout << "Disconnecting from MySQL database..." << endl;
isConnected = false;

Page 15 of 47
logActivity("Disconnected from MySQL");
}

bool executeQuery(const string& query) override {


if (!isConnected) {
cout << "Error: Not connected to database" << endl;
return false;
}
cout << "Executing MySQL query: " << query << endl;
return true;
}
};

class PostgreSQLDatabase : public Database {


private:
string host, port, database;
bool isConnected;

public:
PostgreSQLDatabase(string h, string p, string db)
: host(h), port(p), database(db), isConnected(false) {}

void connect() override {


cout << "Connecting to PostgreSQL database at " << host << ":" <<
isConnected = true;
logActivity("Connected to PostgreSQL");
}

void disconnect() override {


cout << "Disconnecting from PostgreSQL database..." << endl;
isConnected = false;
logActivity("Disconnected from PostgreSQL");
}

bool executeQuery(const string& query) override {


if (!isConnected) {
cout << "Error: Not connected to database" << endl;
return false;
}
cout << "Executing PostgreSQL query: " << query << endl;
return true;
}
};

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.

Real-World Example: ATM System

// Abstract interface for payment processing


class PaymentProcessor {
public:
virtual bool processPayment(double amount, const string& method) = 0;
virtual bool verifyCredentials(const string& credentials) = 0;
virtual void generateReceipt(double amount, const string& transaction
virtual ~PaymentProcessor() = default;
};

// Concrete implementation - user doesn't need to know internal details


class ATMProcessor : public PaymentProcessor {
private:
// Complex internal mechanisms (abstracted away)
bool validateCard(const string& cardNumber) {
// Complex validation logic
return cardNumber.length() == 16;
}

bool checkPinSecurity(const string& pin) {


// Security checks
return pin.length() == 4;
}

bool communicateWithBank(double amount) {


// Complex network communication
cout << "Communicating with bank..." << endl;
return true;
}

void updateAccountBalance(double amount) {


// Database operations
cout << "Updating account balance..." << endl;
}

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

bool verifyCredentials(const string& credentials) override {


// User only provides credentials, complex verification is hidden
return validateCard(credentials) && checkPinSecurity("1234");
}

void generateReceipt(double amount, const string& transactionId) over


cout << "\n--- ATM Receipt ---" << endl;
cout << "Transaction ID: " << transactionId << endl;
cout << "Amount: $" << amount << endl;
cout << "Date: " << __DATE__ << endl;
cout << "Thank you for using our ATM!" << endl;
cout << "-------------------\n" << endl;
}
};

// High-level abstraction for ATM operations


class ATM {
private:
unique_ptr<PaymentProcessor> processor;
double currentBalance;

public:
ATM() : processor(make_unique<ATMProcessor>()), currentBalance(1000.0

// Simple user interface - complexity is abstracted


void withdrawMoney(double amount, const string& credentials) {
if (processor->verifyCredentials(credentials)) {
if (amount <= currentBalance) {
if (processor->processPayment(amount, "Withdrawal")) {
currentBalance -= amount;
processor->generateReceipt(amount, "TXN" + to_string(

Page 18 of 47
cout << "Please take your cash: $" << amount << endl;
}
} else {
cout << "Insufficient funds!" << endl;
}
} else {
cout << "Invalid credentials!" << endl;
}
}

void checkBalance(const string& credentials) {


if (processor->verifyCredentials(credentials)) {
cout << "Current balance: $" << currentBalance << endl;
} else {
cout << "Invalid credentials!" << endl;
}
}
};

Abstraction through Interfaces

// Interface for different types of media players


class MediaPlayer {
public:
virtual void play(const string& filename) = 0;
virtual void pause() = 0;
virtual void stop() = 0;
virtual void setVolume(int volume) = 0;
virtual ~MediaPlayer() = default;
};

// Abstract base class with some common functionality


class BaseMediaPlayer : public MediaPlayer {
protected:
bool isPlaying;
bool isPaused;
int currentVolume;
string currentFile;

public:
BaseMediaPlayer() : isPlaying(false), isPaused(false), currentVolume(

Page 19 of 47
void pause() override {
if (isPlaying) {
isPaused = !isPaused;
cout << (isPaused ? "Paused" : "Resumed") << " playback" << e
}
}

void stop() override {


isPlaying = false;
isPaused = false;
cout << "Stopped playback" << endl;
}

void setVolume(int volume) override {


currentVolume = max(0, min(100, volume));
cout << "Volume set to " << currentVolume << "%" << endl;
}
};

// 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 <<
}
};

class VideoPlayer : public BaseMediaPlayer {


public:
void play(const string& filename) override {
string extension = filename.substr(filename.length() - 4);
if (extension != ".mp4" && extension != ".avi") {
cout << "Error: VideoPlayer supports .mp4 and .avi files only
return;
}

Page 20 of 47
currentFile = filename;
isPlaying = true;
isPaused = false;
cout << "Playing video: " << filename << " with audio at " << cur
}
};

7. Advanced OOP Concepts

Friend Functions and Classes

class BankAccount; // Forward declaration

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) {}

// Declare friend function


friend void BankManager::auditAccount(const BankAccount& account);
friend double BankManager::calculateInterest(const BankAccount& accou

// Friend class - entire class has access to private members


friend class AccountingSystem;

void deposit(double amount) {


balance += amount;
transactionHistory.push_back("Deposited: $" + to_string(amount));
}

Page 21 of 47
bool withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
transactionHistory.push_back("Withdrew: $" + to_string(amount
return true;
}
return false;
}

double getBalance() const { return balance; }


};

// Friend function implementations


void BankManager::auditAccount(const BankAccount& account) {
cout << "Auditing Account: " << account.accountNumber << endl;
cout << "Balance: $" << account.balance << endl;
cout << "Transaction History:" << endl;
for (const auto& transaction : account.transactionHistory) {
cout << " " << transaction << endl;
}
}

double BankManager::calculateInterest(const BankAccount& account, double


return account.balance * rate / 100;
}

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

// Static member function


static int getTotalEmployees() {
return totalEmployees;
}

static double getAverageSalary() {


if (totalEmployees > 0) {
return totalSalary / totalEmployees;
}
return 0;
}

static void displayCompanyStats() {


cout << "\n--- Company Statistics ---" << endl;
cout << "Total Employees: " << totalEmployees << endl;
cout << "Total Salary Expense: $" << totalSalary << endl;
cout << "Average Salary: $" << getAverageSalary() << endl;
cout << "-------------------------\n" << endl;
}

void displayInfo() const {


cout << "ID: " << employeeId << ", Name: " << name << ", Salary:
}
};

Page 23 of 47
// Static member definitions (required outside class)
int Employee::totalEmployees = 0;
double Employee::totalSalary = 0.0;

Composition vs Aggregation

// Composition - "Has-a" relationship (strong ownership)


class Engine {
private:
int cylinders;
double displacement;

public:
Engine(int cyl, double disp) : cylinders(cyl), displacement(disp) {}

void start() {
cout << cylinders << "-cylinder engine started" << endl;
}

double getDisplacement() const { return displacement; }


};

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

// Aggregation - "Uses-a" relationship (weak ownership)


class Driver {
private:
string name;
string licenseNumber;

public:
Driver(string n, string license) : name(n), licenseNumber(license) {}

void drive() {
cout << "Driver " << name << " is driving" << endl;
}

string getName() const { return name; }


};

class Taxi {
private:
string plateNumber;
Driver* currentDriver; // Aggregation - Driver exists independentl

public:
Taxi(string plate) : plateNumber(plate), currentDriver(nullptr) {}

void assignDriver(Driver* driver) {


currentDriver = driver;
cout << "Driver " << driver->getName() << " assigned to taxi " <<
}

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

8. Memory Management in OOP

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

string getName() const { return name; }


};

// Smart pointer examples


void demonstrateSmartPointers() {
cout << "\n=== Smart Pointers Demo ===" << endl;

// 1. unique_ptr - exclusive ownership


{
unique_ptr<Resource> resource1 = make_unique<Resource>("UniqueRes
resource1->process();

// Transfer ownership

Page 26 of 47
unique_ptr<Resource> resource2 = move(resource1);
// resource1 is now nullptr

if (resource2) {
resource2->process();
}
} // resource2 automatically deleted here

// 2. shared_ptr - shared ownership


{
shared_ptr<Resource> shared1 = make_shared<Resource>("SharedResou
cout << "Reference count: " << shared1.use_count() << endl;

{
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

cout << "Reference count: " << shared1.use_count() << endl;


} // Last reference goes out of scope, resource deleted

// 3. weak_ptr - non-owning reference


weak_ptr<Resource> weakRef;
{
shared_ptr<Resource> shared = make_shared<Resource>("WeakResource
weakRef = shared; // weak_ptr doesn't increase ref count

if (auto locked = weakRef.lock()) { // Convert to shared_ptr if


locked->process();
}
} // shared goes out of scope, resource deleted

// Try to access through weak_ptr


if (weakRef.expired()) {
cout << "Weak reference is expired" << endl;
}
}

// RAII (Resource Acquisition Is Initialization) example


class FileHandler {
private:
unique_ptr<FILE, decltype(&fclose)> file;
string filename;

Page 27 of 47
public:
FileHandler(const string& fname)
: file(nullptr, &fclose), filename(fname) {

FILE* f = fopen(filename.c_str(), "w");


if (!f) {
throw runtime_error("Cannot open file: " + filename);
}
file.reset(f);
cout << "File opened: " << filename << endl;
}

~FileHandler() {
cout << "File closed: " << filename << endl;
}

void write(const string& content) {


if (file) {
fprintf(file.get(), "%s\n", content.c_str());
fflush(file.get());
}
}
};

Copy and Move Semantics

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";
}

// Copy assignment operator


LargeObject& operator=(const LargeObject& other) {
if (this != &other) {
name = other.name + "_assigned";
data = other.data;
cout << "Copy assignment: " << name << endl;
}
return *this;
}

// Move assignment operator


LargeObject& operator=(LargeObject&& other) noexcept {
if (this != &other) {
name = move(other.name);
data = move(other.data);
cout << "Move assignment: " << name << endl;
other.name = "moved_from";
}
return *this;
}

// Destructor
~LargeObject() {
cout << "Destructor: " << name << endl;
}

void display() const {


cout << "Object: " << name << " (data size: " << data.size() << "
}
};

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();

// Using move in containers


vector<LargeObject> objects;
objects.push_back(LargeObject("Temp1", 500000)); // Move constructor
objects.emplace_back("Temp2", 500000); // Constructed in-p
}

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();
}

void log(const string& level, const string& message) {


lock_guard<mutex> lock(mtx);
auto now = chrono::system_clock::now();
auto time_t = chrono::system_clock::to_time_t(now);

fileStream << "[" << put_time(localtime(&time_t), "%Y-%m-%d %H:%M


<< "] [" << level << "] " << message << endl;
fileStream.flush();

cout << "[" << level << "] " << message << endl;
}

void info(const string& message) { log("INFO", message); }


void warning(const string& message) { log("WARNING", message); }
void error(const string& message) { log("ERROR", message); }

~Logger() {
if (fileStream.is_open()) {
fileStream.close();
}
}
};

// Static member definitions


unique_ptr<Logger> Logger::instance = nullptr;
mutex Logger::mtx;

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) {}

void start() override {


cout << "Car " << model << " engine started" << endl;
}

void stop() override {


cout << "Car " << model << " engine stopped" << endl;
}

double getPrice() const override { return price; }


string getType() const override { return "Car: " + model; }
};

class Motorcycle : public Vehicle {


private:
string brand;
double price;

public:
Motorcycle(const string& b, double p) : brand(b), price(p) {}

void start() override {


cout << "Motorcycle " << brand << " engine roared to life" << end
}

void stop() override {


cout << "Motorcycle " << brand << " engine stopped" << endl;
}

double getPrice() const override { return price; }

Page 32 of 47
string getType() const override { return "Motorcycle: " + brand; }
};

class Truck : public Vehicle {


private:
string model;
double price;
int loadCapacity;

public:
Truck(const string& m, double p, int capacity)
: model(m), price(p), loadCapacity(capacity) {}

void start() override {


cout << "Truck " << model << " diesel engine started" << endl;
}

void stop() override {


cout << "Truck " << model << " engine stopped" << endl;
}

double getPrice() const override { return price; }


string getType() const override {
return "Truck: " + model + " (" + to_string(loadCapacity) + " ton
}
};

// Factory class
class VehicleFactory {
public:
enum class VehicleType {
CAR,
MOTORCYCLE,
TRUCK
};

static unique_ptr<Vehicle> createVehicle(VehicleType type,


const string& model,
double price,
int capacity = 0) {
switch (type) {
case VehicleType::CAR:
return make_unique<Car>(model, price);

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");
}
}

// Factory method with string parameter


static unique_ptr<Vehicle> createVehicle(const string& type,
const string& model,
double price,
int capacity = 0) {
if (type == "car") {
return createVehicle(VehicleType::CAR, model, price);
} else if (type == "motorcycle") {
return createVehicle(VehicleType::MOTORCYCLE, model, price);
} else if (type == "truck") {
return createVehicle(VehicleType::TRUCK, model, price, capaci
} else {
throw invalid_argument("Unknown vehicle type: " + type);
}
}
};

// Usage example
void demonstrateFactory() {
cout << "\n=== Factory Pattern Demo ===" << endl;

vector<unique_ptr<Vehicle>> vehicles;

// Create vehicles using factory


vehicles.push_back(VehicleFactory::createVehicle(VehicleFactory::Vehi
vehicles.push_back(VehicleFactory::createVehicle("motorcycle", "Harle
vehicles.push_back(VehicleFactory::createVehicle("truck", "Ford F-150

// Use vehicles polymorphically


for (auto& vehicle : vehicles) {
cout << "\nVehicle: " << vehicle->getType() << " - $" << vehicle-
vehicle->start();
vehicle->stop();

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

// Concrete subject - News Agency


class NewsAgency : public Subject {
private:
vector<Observer*> observers;
string latestNews;

public:
void attach(Observer* observer) override {
observers.push_back(observer);
cout << observer->getName() << " subscribed to news updates" << e
}

void detach(Observer* observer) override {


auto it = find(observers.begin(), observers.end(), observer);
if (it != observers.end()) {
observers.erase(it);
cout << observer->getName() << " unsubscribed from news updat
}
}

Page 35 of 47
void notify(const string& message) override {
cout << "\nNotifying all subscribers about: " << message << endl;
for (Observer* observer : observers) {
observer->update(message);
}
}

void publishNews(const string& news) {


latestNews = news;
notify(news);
}

string getLatestNews() const { return latestNews; }


};

// Concrete observers
class NewsChannel : public Observer {
private:
string channelName;
vector<string> newsHistory;

public:
NewsChannel(const string& name) : channelName(name) {}

void update(const string& message) override {


newsHistory.push_back(message);
cout << "[" << channelName << "] Broadcasting: " << message << en
}

string getName() const override { return channelName; }

void showHistory() const {


cout << "\n" << channelName << " News History:" << endl;
for (size_t i = 0; i < newsHistory.size(); ++i) {
cout << (i + 1) << ". " << newsHistory[i] << endl;
}
}
};

class OnlinePortal : public Observer {


private:
string portalName;
string currentHeadline;

Page 36 of 47
public:
OnlinePortal(const string& name) : portalName(name) {}

void update(const string& message) override {


currentHeadline = message;
cout << "[" << portalName << "] Website updated with: " << messag
sendPushNotification(message);
}

string getName() const override { return portalName; }

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");

// Unsubscribe one observer


agency.detach(&bbc);

agency.publishNews("Weather: Sunny skies expected throughout the week

// Show history

Page 37 of 47
cnn.showHistory();
}

10. Common Interview Questions

Question 1: Explain the four pillars of OOP with examples

Answer:

1. Encapsulation: Bundling data and methods together while controlling access.

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

2. Inheritance: Creating new classes based on existing ones.

class Animal {
public:
virtual void makeSound() = 0;
};

class Dog : public Animal {


public:
void makeSound() override { cout << "Woof!" << endl; }
};

3. Polymorphism: One interface, multiple implementations.

void playWith(Animal* animal) {


animal->makeSound(); // Different sound based on actual object type
}

4. Abstraction: Hiding complex details, showing only essential features.

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();
};

Question 2: What's the difference between function overloading and


function overriding?

Answer:

Function Overloading (Compile-time polymorphism):

Same function name, different parameters


Resolved at compile time
Can be in same class

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

Function Overriding (Runtime polymorphism):

Same function signature in base and derived classes


Base function must be virtual
Resolved at runtime

class Animal {
public:
virtual void speak() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {


public:

Page 39 of 47
void speak() override { cout << "Woof!" << endl; }
};

Question 3: Explain virtual functions and virtual destructors

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

class Circle : public Shape {


private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
void display() const override { cout << "Circle with area " << area()
};

// 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;
}
};

// Without virtual destructor, only Base destructor would be called


Base* obj = new Derived();
delete obj; // Calls both Derived and Base destructors

Question 4: What's the difference between composition and


inheritance?

Answer:

Inheritance ("is-a" relationship):

class Animal {
protected:
string name;
public:
virtual void eat() { cout << name << " is eating" << endl; }
};

class Dog : public Animal { // Dog IS-A Animal


public:
Dog(string n) { name = n; }
void bark() { cout << name << " is barking" << endl; }
};

Composition ("has-a" relationship):

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

Question 5: Explain copy constructor and assignment operator

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

// Copy constructor - called when object is created as copy of anothe


MyClass(const MyClass& other) : size(other.size), data(new int[other.
copy(other.data, other.data + size, data);
cout << "Copy constructor called" << endl;
}

// Assignment operator - called when existing object is assigned


MyClass& operator=(const MyClass& other) {
cout << "Assignment operator called" << endl;
if (this != &other) { // Self-assignment check
delete[] data; // Clean up existing resources

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

Question 6: What are abstract classes and interfaces?

Answer:

Abstract Class:

Contains at least one pure virtual function


Cannot be instantiated
Can have both pure virtual and regular functions
Can have data members

class Animal { // Abstract class


protected:
string species;

public:
Animal(string s) : species(s) {}

// Pure virtual function makes this abstract


virtual void makeSound() const = 0;

// Regular virtual function

Page 43 of 47
virtual void sleep() const {
cout << species << " is sleeping" << endl;
}

// Non-virtual function
void breathe() const {
cout << species << " is breathing" << endl;
}
};

class Dog : public Animal {


public:
Dog() : Animal("Canine") {}

void makeSound() const override {


cout << "Woof! Woof!" << endl;
}
};

Interface (Pure Abstract Class):

All functions are pure virtual


No data members (or only static const)
Defines a contract for implementing classes

class Drawable { // Interface


public:
virtual void draw() const = 0;
virtual void resize(double scale) = 0;
virtual double getArea() const = 0;
virtual ~Drawable() = default;
};

class Circle : public Drawable {


private:
double radius;

public:
Circle(double r) : radius(r) {}

void draw() const override {


cout << "Drawing circle with radius " << radius << endl;
}

Page 44 of 47
void resize(double scale) override {
radius *= scale;
}

double getArea() const override {


return 3.14159 * radius * radius;
}
};

Question 7: What is the diamond problem and how is it solved?

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

class Mammal : public Animal {


public:
Mammal(string n) : Animal(n) {}
void giveBirth() { cout << "Giving birth to live young" << endl; }
};

class Bird : public Animal {


public:
Bird(string n) : Animal(n) {}
void layEggs() { cout << "Laying eggs" << endl; }
};

// Diamond problem: Bat inherits Animal twice


class Bat : public Mammal, public Bird {
public:
Bat(string n) : Mammal(n), Bird(n) {} // Error: ambiguous
// void eat() - which Animal::eat() to use?
};

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

class Mammal : virtual public Animal { // Virtual inheritance


public:
Mammal(string n) : Animal(n) {}
void giveBirth() { cout << "Giving birth" << endl; }
};

class Bird : virtual public Animal { // Virtual inheritance


public:
Bird(string n) : Animal(n) {}
void layEggs() { cout << "Laying eggs" << endl; }
};

class Bat : public Mammal, public Bird {


public:
// Most derived class must call virtual base constructor directly
Bat(string n) : Animal(n), Mammal(n), Bird(n) {}

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

Practice Problems for Interviews

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:

1. Classes and Objects - The fundamental building blocks


2. Encapsulation - Data hiding and controlled access
3. Inheritance - Code reuse and hierarchical relationships
4. Polymorphism - One interface, multiple implementations
5. Abstraction - Hiding complexity, exposing essentials
6. Advanced Features - Smart pointers, RAII, design patterns

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:

Always prefer composition over inheritance when possible


Use virtual destructors in base classes
Implement RAII for resource management
Apply SOLID principles in your designs
Choose appropriate design patterns for specific problems
Practice writing clean, maintainable OOP code

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

You might also like