C/C++
OOP in C++ Explained: Classes to Polymorphism
Object-oriented programming (OOP) organizes code around objects rather than procedures. C++ supports OOP natively and is one of the most widely used languages for teaching the paradigm. This post covers the 4 core pillars (objects, classes, inheritance, polymorphism, encapsulation, and abstraction) with working C++ examples for each.

Objects
An object is a self-contained unit that pairs data (attributes) with the functions that operate on that data (methods). Objects are the building blocks of OOP and give you a way to model real-world entities directly in code.
A car, for example, can be an object. Its attributes are color, make, model, and year. Its methods are accelerate(), brake(), and turn(). The data and the behavior live together in one unit.
Classes
A class is the blueprint for creating objects. It declares the data members (attributes) and member functions (methods) that every object of that class carries.
In C++, the class keyword defines a class. Here is a Person class with 2 data members and 4 member functions:
class Person {
private:
string name;
int age;
public:
void setName(string n) {
name = n;
}
void setAge(int a) {
age = a;
}
string getName() {
return name;
}
int getAge() {
return age;
}
};
name and age are private so outside code cannot touch them directly. The 4 public functions (setName, setAge, getName, getAge) form the controlled interface.
Create an object from that class and call its methods like this:
Person p;
p.setName("Alice");
p.setAge(25);
string name = p.getName(); // "Alice"
int age = p.getAge(); // 25
OOP in C++ is built on 4 pillars: Inheritance, Polymorphism, Encapsulation, and Abstraction.
Inheritance
Inheritance lets one class acquire the properties of another. The class that inherits is the derived class (or subclass); the class it inherits from is the base class (or superclass).
Use the public access specifier to inherit publicly from a base class. Here, Student inherits from Person and adds a rollNumber field:
class Student : public Person {
private:
int rollNumber;
public:
void setRollNumber(int r) {
rollNumber = r;
}
int getRollNumber() {
return rollNumber;
}
};
Student inherits name and age from Person, so you call setName, setAge, and the new roll-number methods on the same object:
Student s;
s.setName("Mary");
s.setAge(20);
s.setRollNumber(101);
string name = s.getName(); // "Mary"
int age = s.getAge(); // 20
int rollNumber = s.getRollNumber(); // 101
Polymorphism
Polymorphism lets a single interface work across objects of different derived types. C++ has 2 forms.
Compile-time polymorphism (static) is resolved at compile time via function overloading and operator overloading. The compiler picks the right overload based on the argument types.
Runtime polymorphism (dynamic) is resolved at runtime via virtual functions and function overriding. Declare a function virtual in the base class; derived classes override it. The correct version runs based on the actual object type, not the pointer type.
Here is printDetails declared virtual in Person and overridden in Student:
class Person {
private:
string name;
int age;
public:
void setName(string n) { name = n; }
void setAge(int a) { age = a; }
string getName() { return name; }
int getAge() { return age; }
virtual void printDetails() {
cout << "Name: " << name << endl;
cout << "Age: " << age << endl;
}
};
class Student : public Person {
private:
int rollNumber;
public:
void setRollNumber(int r) { rollNumber = r; }
int getRollNumber() { return rollNumber; }
void printDetails() override {
cout << "Name: " << getName() << endl;
cout << "Age: " << getAge() << endl;
cout << "Roll Number: " << getRollNumber() << endl;
}
};
Call printDetails through a base-class pointer and the runtime dispatches to the correct override:
Person* p = new Person();
Student* s = new Student();
p->setName("John"); p->setAge(30);
s->setName("Mary"); s->setAge(20); s->setRollNumber(101);
p->printDetails(); // prints Name + Age
s->printDetails(); // prints Name + Age + Roll Number
Get help with your C++ assignment from working developers
Encapsulation
Encapsulation combines data and the functions that manage it into a single class, and restricts direct access to the data from outside. Private members are the enforcement mechanism.
A BankAccount class demonstrates this. The balance field is private; the only way to change it is through deposit and withdraw:
class BankAccount {
private:
float balance;
public:
BankAccount() {
balance = 0;
}
void deposit(float amount) {
balance += amount;
}
void withdraw(float amount) {
if (balance >= amount) {
balance -= amount;
}
}
float getBalance() {
return balance;
}
};
balance is readable via getBalance() and writable only through deposit() or withdraw(). Outside code cannot accidentally set balance = -999:
BankAccount account;
account.deposit(1000);
cout << account.getBalance(); // 1000
account.withdraw(500);
cout << account.getBalance(); // 500
Abstraction
Abstraction hides implementation details behind a common interface. In C++, pure virtual functions create abstract classes that define what derived classes must implement, without specifying how.
Declare a function as = 0 to make it pure virtual. A class with at least one pure virtual function is abstract and cannot be instantiated directly:
class Shape {
public:
virtual float area() = 0;
virtual float perimeter() = 0;
};
Derived classes inherit Shape and provide concrete implementations:
class Rectangle : public Shape {
private:
float length;
float width;
public:
Rectangle(float l, float w) : length(l), width(w) {}
float area() override {
return length * width;
}
float perimeter() override {
return 2 * (length + width);
}
};
A Circle or Triangle class would follow the same pattern, each providing its own area() and perimeter(). Code that works with Shape* pointers works with any of them without knowing the concrete type.
Practical Design Notes
4 habits that save debugging time when writing OOP code in C++:
- Design class responsibilities first. Decide what each class owns and what it delegates before writing any code. A class that does too many things is the most common cause of tangled inheritance chains.
- Use access specifiers to enforce boundaries.
privateon data members andpublicon the interface is the baseline. Only expose what callers actually need. - Name classes and methods after what they represent.
getBalance()is clear.getValue()on the same class is not. - Avoid global variables inside class-heavy code. Pass state through constructors or method parameters. Global variables and OOP class design work against each other.
For a deeper look at when to use inheritance versus other patterns, see Inheritance vs Composition and C++ Best Practices: How to Write Code Like a Pro.
OOP in C++ applies directly to coursework in data structures, algorithms, and systems programming. If you are stuck on a class design problem or an assignment involving virtual dispatch, the developers at C++ Programming Assignment Help work through these exact problems daily.
Related articles
- C/C++
Min Heap and Max Heap in C++
Build min heaps and max heaps in C++ with std::priority_queue and the STL heap algorithms, plus array math, heapify, heap sort, and worked examples.
Sep 19, 2023
- C/C++
Python vs C++: Which Should You Learn?
A direct comparison of Python and C++ across syntax, speed, memory management, OOP, and use cases to help you pick the right language.
Sep 17, 2023
- C/C++
Vectors in C++: A Complete Guide
Master std::vector in C++ with declaration, push_back and emplace_back, iterators, size vs capacity, 2D vectors, custom types, and complexity, with code that compiles.
Aug 7, 2023


