OOPs Concepts in C++
Object-Oriented Programming (OOP) is a programming paradigm based on the concept of “objects” which contain data and code. C++ supports four fundamental OOP principles.
The Four Pillars of OOP
- Encapsulation - Bundling data and methods together
- Abstraction - Hiding complex implementation details
- Inheritance - Creating new classes from existing ones
- Polymorphism - Using a single interface for different data types
1. Encapsulation
Encapsulation is the bundling of data and methods that operate on that data within a single unit (class), and restricting direct access to some components.
Benefits:
- Data hiding and security
- Flexibility and maintainability
- Control over data
Example:
#include <iostream>
#include <string>
using namespace std;
class Employee {
private:
string name;
double salary;
public:
// Setter methods
void setName(string n) {
name = n;
}
void setSalary(double s) {
if (s >= 0) { // Validation
salary = s;
}
}
// Getter methods
string getName() {
return name;
}
double getSalary() {
return salary;
}
};
int main() {
Employee emp;
emp.setName("John");
emp.setSalary(50000);
cout << "Employee: " << emp.getName() << endl;
cout << "Salary: $" << emp.getSalary() << endl;
return 0;
}Output:
Employee: John
Salary: $500002. Abstraction
Abstraction means hiding complex implementation details and showing only essential features of an object.
Benefits:
- Reduces complexity
- Enhances code reusability
- Improves maintainability
Example using Abstract Class:
#include <iostream>
using namespace std;
// Abstract class
class Shape {
public:
// Pure virtual function (abstract method)
virtual double getArea() = 0;
virtual void display() = 0;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() override {
return 3.14159 * radius * radius;
}
void display() override {
cout << "Circle with radius: " << radius << endl;
cout << "Area: " << getArea() << endl;
}
};
class Rectangle : public Shape {
private:
double length, width;
public:
Rectangle(double l, double w) : length(l), width(w) {}
double getArea() override {
return length * width;
}
void display() override {
cout << "Rectangle " << length << "x" << width << endl;
cout << "Area: " << getArea() << endl;
}
};
int main() {
Circle circle(5);
Rectangle rect(4, 6);
circle.display();
cout << endl;
rect.display();
return 0;
}Output:
Circle with radius: 5
Area: 78.5398
Rectangle 4x6
Area: 243. Inheritance
Inheritance allows a class to inherit properties and methods from another class.
Types of Inheritance:
- Single Inheritance
- Multiple Inheritance
- Multilevel Inheritance
- Hierarchical Inheritance
- Hybrid Inheritance
Example:
#include <iostream>
#include <string>
using namespace std;
// Base class
class Animal {
protected:
string name;
public:
Animal(string n) : name(n) {}
void eat() {
cout << name << " is eating." << endl;
}
void sleep() {
cout << name << " is sleeping." << endl;
}
};
// Derived class
class Dog : public Animal {
public:
Dog(string n) : Animal(n) {}
void bark() {
cout << name << " is barking: Woof! Woof!" << endl;
}
};
// Another derived class
class Cat : public Animal {
public:
Cat(string n) : Animal(n) {}
void meow() {
cout << name << " is meowing: Meow! Meow!" << endl;
}
};
int main() {
Dog dog("Buddy");
dog.eat();
dog.bark();
cout << endl;
Cat cat("Whiskers");
cat.sleep();
cat.meow();
return 0;
}Output:
Buddy is eating.
Buddy is barking: Woof! Woof!
Whiskers is sleeping.
Whiskers is meowing: Meow! Meow!Learn more about Inheritance in detail.
4. Polymorphism
Polymorphism means “many forms”. It allows objects of different classes to be treated as objects of a common base class.
Types:
- Compile-time Polymorphism (Function Overloading, Operator Overloading)
- Runtime Polymorphism (Virtual Functions)
Function Overloading Example:
#include <iostream>
using namespace std;
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;
}
};
int main() {
Calculator calc;
cout << "10 + 20 = " << calc.add(10, 20) << endl;
cout << "5.5 + 3.3 = " << calc.add(5.5, 3.3) << endl;
cout << "1 + 2 + 3 = " << calc.add(1, 2, 3) << endl;
return 0;
}Output:
10 + 20 = 30
5.5 + 3.3 = 8.8
1 + 2 + 3 = 6Runtime Polymorphism Example:
#include <iostream>
using namespace std;
class Animal {
public:
virtual void makeSound() {
cout << "Some generic animal sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof! Woof!" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow! Meow!" << endl;
}
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->makeSound(); // Calls Dog's makeSound()
animal2->makeSound(); // Calls Cat's makeSound()
delete animal1;
delete animal2;
return 0;
}Output:
Woof! Woof!
Meow! Meow!Real-World Banking System Example
Combining all OOP concepts:
#include <iostream>
#include <string>
using namespace std;
// Abstract base class (Abstraction)
class Account {
protected:
string accountNumber;
string holderName;
double balance;
public:
Account(string accNum, string name, double bal)
: accountNumber(accNum), holderName(name), balance(bal) {}
// Encapsulation - getters
string getAccountNumber() { return accountNumber; }
string getHolderName() { return holderName; }
double getBalance() { return balance; }
// Pure virtual function (Abstraction)
virtual void displayInfo() = 0;
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "Deposited: $" << amount << endl;
}
}
virtual void withdraw(double amount) = 0;
};
// Derived class (Inheritance)
class SavingsAccount : public Account {
private:
double interestRate;
public:
SavingsAccount(string accNum, string name, double bal, double rate)
: Account(accNum, name, bal), interestRate(rate) {}
void displayInfo() override {
cout << "=== Savings Account ===" << endl;
cout << "Account Number: " << accountNumber << endl;
cout << "Holder: " << holderName << endl;
cout << "Balance: $" << balance << endl;
cout << "Interest Rate: " << interestRate << "%" << endl;
}
void withdraw(double amount) override {
if (amount > 0 && amount <= balance) {
balance -= amount;
cout << "Withdrawn: $" << amount << endl;
} else {
cout << "Insufficient balance!" << endl;
}
}
void addInterest() {
double interest = balance * (interestRate / 100);
balance += interest;
cout << "Interest added: $" << interest << endl;
}
};
// Another derived class (Inheritance)
class CheckingAccount : public Account {
private:
double overdraftLimit;
public:
CheckingAccount(string accNum, string name, double bal, double overdraft)
: Account(accNum, name, bal), overdraftLimit(overdraft) {}
void displayInfo() override {
cout << "=== Checking Account ===" << endl;
cout << "Account Number: " << accountNumber << endl;
cout << "Holder: " << holderName << endl;
cout << "Balance: $" << balance << endl;
cout << "Overdraft Limit: $" << overdraftLimit << endl;
}
void withdraw(double amount) override {
if (amount > 0 && (balance + overdraftLimit) >= amount) {
balance -= amount;
cout << "Withdrawn: $" << amount << endl;
} else {
cout << "Exceeds overdraft limit!" << endl;
}
}
};
int main() {
SavingsAccount savings("SAV001", "Alice", 1000, 3.5);
savings.displayInfo();
savings.deposit(500);
savings.addInterest();
cout << "New Balance: $" << savings.getBalance() << endl;
cout << "\n";
CheckingAccount checking("CHK001", "Bob", 500, 200);
checking.displayInfo();
checking.withdraw(600); // Uses overdraft
cout << "New Balance: $" << checking.getBalance() << endl;
return 0;
}Benefits of OOP
- Modularity: Code is organized into independent objects
- Reusability: Classes can be reused across different programs
- Maintainability: Easy to update and modify
- Security: Data hiding through encapsulation
- Scalability: Easy to add new features
OOP vs Procedural Programming
| OOP | Procedural |
|---|---|
| Based on objects | Based on functions |
| Data and methods together | Separate data and functions |
| Bottom-up approach | Top-down approach |
| More secure (encapsulation) | Less secure |
| Example: C++, Java | Example: C, Pascal |
Best Practices
- Use encapsulation: Keep data members private
- Program to an interface: Use abstract classes/interfaces
- Favor composition over inheritance: When possible
- Keep classes focused: Single Responsibility Principle
- Use meaningful names: Clear class and method names
Practice Exercises
-
Library System: Create classes for Book, Member, and Library with proper encapsulation
-
Shape Hierarchy: Implement abstract Shape class with Circle, Rectangle, Triangle
-
Vehicle System: Create base Vehicle class and derive Car, Bike, Truck with polymorphism
-
Employee Management: Implement Employee hierarchy with different types (Manager, Developer, etc.)
-
E-commerce: Design Product, Cart, and Order classes with proper OOP principles
OOP principles are essential for writing professional, maintainable C++ code!