Object-Oriented Programming in C++ Explained: T...
Object oriented programming in C++ is a way of structuring code around objects: bundles of data and the functions that act on that data. Its four pillars are encapsulation, inheritance, polymorphism, and abstraction. Mastering them helps you write C++ that is organized, reusable, and easier to maintain as projects grow.
What is object oriented programming in C++?
In procedural code you write a list of steps and pass loose variables around. In object oriented programming you model real things as objects built from a class. A class is a blueprint; an object is one instance made from that blueprint. Think of a class as the design for a rickshaw and each actual rickshaw on the road as an object.
C++ supports both styles, so you can adopt OOP gradually. A class holds data members (variables) and member functions (behavior) together. This keeps related code in one place instead of scattered across a file. Below is a minimal class to show the shape of the syntax before we explore the four pillars.
#include <iostream>
#include <string>
class Student {
public:
std::string name;
int roll;
void introduce() {
std::cout << "Hi, I am " << name
<< ", roll " << roll << "\n";
}
};
int main() {
Student s; // s is an object
s.name = "Rahim";
s.roll = 12;
s.introduce(); // prints: Hi, I am Rahim, roll 12
return 0;
}
Pillar 1: Encapsulation
Encapsulation means hiding an object's internal data and exposing only safe ways to use it. In C++ you do this with access specifiers: private members are reachable only inside the class, while public members form the interface other code may call. This stops outside code from putting an object into an invalid state.
Below, balance is private, so nobody can set it to a negative number directly. The public deposit and withdraw functions guard every change.
#include <iostream>
class BankAccount {
private:
double balance = 0.0;
public:
void deposit(double amount) {
if (amount > 0) balance += amount;
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false; // not enough money
}
double getBalance() const {
return balance;
}
};
int main() {
BankAccount acc;
acc.deposit(500);
acc.withdraw(200);
std::cout << "Balance: " << acc.getBalance() << "\n"; // 300
}
Because balance is private, the rules live in one place. If you later add a daily withdrawal limit, you change only this class and every caller benefits.
Pillar 2: Inheritance
Inheritance lets a new class reuse and extend an existing one. The existing class is the base class; the new one is the derived class. This models an "is a" relationship: a teacher is a person, so Teacher can inherit from Person and gain its members without copying code.
#include <iostream>
#include <string>
class Person {
public:
std::string name;
void greet() const {
std::cout << "Hello, I am " << name << "\n";
}
};
class Teacher : public Person {
public:
std::string subject;
void teach() const {
std::cout << name << " teaches " << subject << "\n";
}
};
int main() {
Teacher t;
t.name = "Karim"; // inherited from Person
t.subject = "Physics";
t.greet(); // inherited behavior
t.teach(); // own behavior
}
The Teacher class did not redefine name or greet(); it received them from Person. You write shared behavior once in the base class and specialise in derived classes, which cuts duplication and keeps related types consistent.
Pillar 3: Polymorphism
Polymorphism means "many forms": the same function call can behave differently depending on the actual object. In C++ you enable this with virtual functions. A base class declares a virtual function, derived classes override it, and a base-class pointer calls the correct version at run time.
#include <iostream>
class Shape {
public:
virtual double area() const {
return 0.0;
}
virtual ~Shape() {} // virtual destructor for safe cleanup
};
class Circle : public Shape {
double r;
public:
Circle(double radius) : r(radius) {}
double area() const override {
return 3.14159 * r * r;
}
};
class Square : public Shape {
double side;
public:
Square(double s) : side(s) {}
double area() const override {
return side * side;
}
};
int main() {
Shape* shapes[] = { new Circle(2.0), new Square(3.0) };
for (Shape* s : shapes) {
std::cout << "Area: " << s->area() << "\n";
}
for (Shape* s : shapes) delete s; // free memory
}
The loop calls s->area() the same way for every shape, yet each object runs its own version. Add a Triangle later and the loop needs no change. The override keyword asks the compiler to verify you really overrode a base function, catching typos early.
Pillar 4: Abstraction
Abstraction means exposing only the essential operations and hiding the messy details. You decide what an object should do, not how callers must do it. In C++, a common tool is the abstract class: a class with at least one pure virtual function (written = 0). It cannot be instantiated and forces every derived class to supply its own implementation.
#include <iostream>
class PaymentMethod {
public:
virtual void pay(double amount) const = 0; // pure virtual
virtual ~PaymentMethod() {}
};
class BkashPayment : public PaymentMethod {
public:
void pay(double amount) const override {
std::cout << "Paid " << amount << " taka via bKash\n";
}
};
class CardPayment : public PaymentMethod {
public:
void pay(double amount) const override {
std::cout << "Paid " << amount << " taka via card\n";
}
};
void checkout(const PaymentMethod& method, double total) {
method.pay(total); // does not care which method
}
int main() {
BkashPayment b;
CardPayment c;
checkout(b, 750);
checkout(c, 1200);
}
The checkout function depends only on the abstract idea "a payment method can pay". It does not know about bKash or card internals. You can add new payment types without touching checkout, which is the practical payoff of abstraction.
How the four pillars work together
These ideas are not separate tricks; they reinforce one another. Encapsulation protects an object's data. Inheritance shares common structure. Abstraction defines a clean contract through base classes. Polymorphism lets you treat many concrete types through that one contract. Together they let a large C++ codebase grow without small changes rippling everywhere.
A good habit for beginners: start with a clear class and private data, then reach for inheritance or virtual functions only when you actually have several related types. Over-engineering early is a common trap. Write the simplest class that solves today's problem, and refactor as real requirements appear.
Frequently asked questions
Is C++ object oriented or procedural?
C++ is multi-paradigm. It fully supports object oriented programming through classes, inheritance, and virtual functions, but it also allows plain procedural code and generic programming with templates. You can mix styles in one project, which is why many courses in Bangladesh teach C first and then add OOP concepts on top.
What is the difference between a class and an object?
A class is a blueprint that defines data members and member functions. An object is a concrete instance created from that class, holding its own copy of the data. One Student class can produce thousands of student objects, each with a different name and roll number.
Why use private members instead of making everything public?
Private members protect an object's internal state from invalid changes. By exposing only controlled public functions, you keep validation rules in one place. This makes bugs easier to find, because any change to the data must pass through the functions you wrote, not arbitrary outside code.
When should I use a virtual function?
Use a virtual function when you expect derived classes to provide their own version of a behavior and you will call it through a base-class pointer or reference. If you never override a function or never use base-class pointers, a plain non-virtual function is simpler and slightly faster.
Strong C++ and OOP fundamentals open doors to software roles across Dhaka and beyond; if you are job hunting, browse current openings on Avian™ Career.
Object oriented programming rewards practice more than reading. Re-type each example above, change a value, break it on purpose, and read the compiler error. Once encapsulation, inheritance, polymorphism, and abstraction feel natural, you can model almost any problem cleanly in C++.
Categories
- Tutorial
- Announcement
- News