Object-Oriented Programming (OOP) is a programming style that uses objects to represent data and methods to manipulate that data. It allows for the creation of modular and reusable code, making it easier to manage and scale software projects. OOP is fundamental in many programming languages, including C++, which is known for its strong support for this paradigm.
In this blog, we will explore the key concepts of Object-Oriented Programming in C++. We will cover essential principles like encapsulation, inheritance, polymorphism, and abstraction. Additionally, we will compare OOP with procedural programming, discuss its pros and cons, and summarise the main concepts in a table.
What is OOP, and Why Is It Important?
Object-Oriented Programming (OOP) is a programming approach that uses objects to model real-world entities. Each object contains data, known as attributes, and functions, known as methods, that operate on this data. This structure allows for the creation of complex programs by combining simpler objects. OOP is based on four main principles: encapsulation, inheritance, polymorphism, and abstraction.
Key Benefits of OOP:
Code Reusability: Use existing code for new applications, reducing development time.
Modularity: Break down programs into smaller, manageable objects.
Ease of Maintenance: Modify and update code with minimal impact on other parts.
Real-World Modeling: Represent real-world entities and relationships directly in the code.
Get curriculum highlights, career paths, industry insights and accelerate your technology journey.
Download brochure
Key OOPs Concepts in C++
Object-Oriented Programming (OOP) in C++ is built on several key concepts that help in creating structured and efficient code. Understanding these concepts is crucial for anyone looking to master C++ and leverage its full potential.
Class
A class is a blueprint for creating objects. It defines properties and behaviours that the objects created from the class will have. Think of a class as a template that defines the structure and functions of an object.
Example:
In this example, we define a class named ‘Smartphone’ with one property, ‘brand’, which represents the brand of the smartphone.
class Smartphone {
public:
string brand;
};
Object
An object is an instance of a class. It contains real values instead of variables. When a class is defined, no memory is allocated until an object of that class is created.
Example:
In this example, we create an object ‘myPhone’ from the Smartphone class. We set the brand property to “Samsung” and printed it. This shows how objects can be used to store and manipulate data defined by the class.
#include <iostream>
using namespace std;
class Smartphone {
public:
string brand; // Defining the class property
};
int main() {
Smartphone myPhone; // Defining the object of the class
myPhone.brand = "Samsung"; // Assigning the value to the class properties
cout << "Brand: " << myPhone.brand << endl;
return 0;
}
Output:
Brand: Samsung
Constructor
A constructor is a special member function of a class that is automatically called when an object of that class is created. The main purpose of a constructor is to initialise objects. It has the same name as the class and does not have a return type.
Example:
In this example:
We define a constructor for the Smartphone class that takes two parameters: b for brand and m for model.
When we create the myPhone object, the constructor is called with “Samsung” and “Galaxy S21” as arguments, initialising the brand and model properties.
We then print the brand and model properties to show the values assigned by the constructor.
This example demonstrates how constructors are used to initialise objects with specific values at the time of their creation.
#include <iostream>
using namespace std;
class Smartphone {
public:
string brand;
string model; // Constructor with parameters
Smartphone(string b, string m) {
brand = b;
model = m;
}
};
int main() { // Create an object of the Smartphone class
Smartphone myPhone("Samsung", "Galaxy S21");
cout << "Brand: " << myPhone.brand << endl;
cout << "Model: " << myPhone.model << endl;
return 0;
}
Output:
Brand: Samsung
Model: Galaxy S21
Inheritance
Inheritance is a fundamental concept in OOP that allows a class to inherit properties and methods from another class. This helps in reusing existing code and extending functionalities.
There are different types of inheritance in C++:
single inheritance
multiple inheritance
multilevel inheritance
hierarchical inheritance
hybrid inheritance.
Single Inheritance
Single inheritance is when a class (derived class) inherits from one base class. This is the most straightforward form of inheritance.
Example:
We define a base class Smartphone with a property brand and a method displayBrand().
The derived class Model inherits from the Smartphone and adds an additional property model and a method displayModel().
The Model class constructor calls the Smartphone constructor to initialise the brand property.
In the main function, we create an object of the Model class and use it to display the brand and model.
#include <iostream>
using namespace std;
// Base class
class Smartphone {
public:
string brand;
// Constructor for base class
Smartphone(string b) {
brand = b;
}
// Method to display the brand
void displayBrand() {
cout << "Brand: " << brand << endl;
}
};
// Derived class
class Model : public Smartphone {
public:
string model;
// Constructor for derived class
Model(string b, string m) : Smartphone(b) {
model = m;
}
// Method to display the model
void displayModel() {
cout << "Model: " << model << endl;
}
};
int main() {
// Create an object of the derived class
Model myPhone("Samsung", "Galaxy S21");
// Call methods to display brand and model
myPhone.displayBrand(); // Output: Brand: Samsung
myPhone.displayModel(); // Output: Model: Galaxy S21
return 0;
}
Output:
Brand: Samsung
Model: Galaxy S21
Multiple Inheritance
Multiple inheritance is when a class inherits from more than one base class. This allows a derived class to inherit features from multiple base classes.
Example:
We define two base classes, Brand and Model, each with its own constructor.
The derived class Smartphone inherits from both Brand and Model.
The Smartphone constructor initialises the brand and model properties by calling the constructors of the Brand and Model.
In the main function, we create an object of the Smartphone class and use it to display the brand and model.
#include <iostream>
using namespace std;
// Base class 1
class Brand {
public:
string brand;
// Constructor for base class 1
Brand(string b) {
brand = b;
}
};
// Base class 2
class Model {
public:
string model;
// Constructor for base class 2
Model(string m) {
model = m;
}
};
// Derived class
class Smartphone : public Brand, public Model {
public:
// Constructor for derived class
Smartphone(string b, string m) : Brand(b), Model(m) {}
// Method to display brand and model
void display() {
cout << "Brand: " << brand << ", Model: " << model << endl;
}
};
int main() {
// Create an object of the derived class
Smartphone myPhone("Samsung", "Galaxy S21");
// Call method to display brand and model
myPhone.display(); // Output: Brand: Samsung, Model: Galaxy S21
return 0;
}
Output:
Brand: Samsung, Model: Galaxy S21
Multilevel Inheritance
Multilevel inheritance is when a class inherits from a derived class, forming a chain of inheritance.
Example:
We define a base class Brand and a derived class Model that inherits from Brand.
The further derived class Smartphone inherits from Model.
The Smartphone constructor initialises the brand and model properties by calling the Model constructor, which in turn calls the Brand constructor.
In the main function, we create an object of the Smartphone class and use it to display the brand and model.
#include <iostream>
using namespace std;
// Base class
class Brand {
public:
string brand;
// Constructor for base class
Brand(string b) {
brand = b;
}
};
// Derived class
class Model : public Brand {
public:
string model;
// Constructor for derived class
Model(string b, string m) : Brand(b) {
model = m;
}
};
// Further derived class
class Smartphone : public Model {
public:
// Constructor for further derived class
Smartphone(string b, string m) : Model(b, m) {}
// Method to display brand and model
void display() {
cout << "Brand: " << brand << ", Model: " << model << endl;
}
};
int main() {
// Create an object of the further derived class
Smartphone myPhone("Samsung", "Galaxy S21");
// Call method to display brand and model
myPhone.display(); // Output: Brand: Samsung, Model: Galaxy S21
return 0;
}
Output:
Brand: Samsung, Model: Galaxy S21
Hierarchical Inheritance
Hierarchical inheritance is when multiple classes inherit from a single base class.
Example:
We define a base class Brand with a property brand and a method displayBrand().
Two derived classes, Model1 and Model2, inherit from Brand.
Each derived class has its own method to display the specific model.
In the main function, we create objects of Model1 and Model2 and use them to display the brand and specific models.
#include <iostream>
using namespace std;
// Base class
class Brand {
public:
string brand;
// Constructor for base class
Brand(string b) {
brand = b;
}
// Method to display the brand
void displayBrand() {
cout << "Brand: " << brand << endl;
}
};
// Derived class 1
class Model1 : public Brand {
public:
Model1(string b) : Brand(b) {}
// Method to display specific model
void displayModel1() {
cout << "Model1: " << brand << endl;
}
};
// Derived class 2
class Model2 : public Brand {
public:
Model2(string b) : Brand(b) {}
// Method to display specific model
void displayModel2() {
cout << "Model2: " << brand << endl;
}
};
int main() {
// Create objects of the derived classes
Model1 phone1("Samsung");
Model2 phone2("Apple");
// Call methods to display brand and specific models
phone1.displayBrand(); // Output: Brand: Samsung
phone1.displayModel1(); // Output: Model1: Samsung
phone2.displayBrand(); // Output: Brand: Apple
phone2.displayModel2(); // Output: Model2: Apple
return 0;
}
Output:
Brand: Samsung
Model1: Samsung
Brand: Apple
Model2: Apple
Hybrid Inheritance
Hybrid inheritance is a combination of two or more types of inheritance.
Example:
We define a base class Brand with a property brand.
Two derived classes, Model1 and Model2, inherit from Brand.
The further derived class Smartphone inherits from both Model1 and Model2.
The Smartphone constructor initialises the brand properties by calling the constructors of Model1 and Model2.
In the main function, we create an object of the Smartphone class and use it to display the brands from both models.
#include <iostream>
using namespace std;
// Base class
class Brand {
public:
string brand;
// Constructor for base class
Brand(string b) {
brand = b;
}
};
// Derived class 1
class Model1 : public Brand {
public:
Model1(string b) : Brand(b) {}
};
// Derived class 2
class Model2 : public Brand {
public:
Model2(string b) : Brand(b) {}
};
// Derived class that inherits from Model1 and Model2
class Smartphone : public Model1, public Model2 {
public:
// Constructor for derived class
Smartphone(string b1, string b2) : Model1(b1), Model2(b2) {}
// Method to display brand from both models
void display() {
cout << "Model1 Brand: " << Model1::brand << ", Model2 Brand: " << Model2::brand << endl;
}
};
int main() {
// Create an object of the derived class
Smartphone myPhone("Samsung", "Apple");
// Call method to display brand from both models
myPhone.display(); // Output: Model1 Brand: Samsung, Model2 Brand: Apple
return 0;
}
Output:
Model1 Brand: Samsung, Model2 Brand: Apple
Polymorphism
Polymorphism is an OOP concept where a single function or method can have different behaviours based on the object that invokes it. In C++, polymorphism is achieved through function overloading, operator overloading, and method overriding using inheritance.
Function Overloading
Function overloading allows multiple functions with the same name but different parameters to coexist.
Example:
The MathOperations class has two add methods with different parameter types.
The correct add method is called based on the argument types provided.
#include <iostream>
using namespace std;
class MathOperations {
public:
// Function to add two integers
int add(int a, int b) {
return a + b;
}
// Function to add two double values
double add(double a, double b) {
return a + b;
}
};
int main() {
MathOperations math;
// Call add function with integers
cout << "Sum of integers: " << math.add(5, 3) << endl; // Output: Sum of integers: 8
// Call add function with doubles
cout << "Sum of doubles: " << math.add(5.5, 3.2) << endl; // Output: Sum of doubles: 8.7
return 0;
}
Output:
Sum of integers: 8
Sum of doubles: 8.7
Operator Overloading
Operator overloading allows you to redefine the functionality of operators for user-defined types.
Example:
The Complex class overloads the + operator to add two complex numbers.
The c1 + c2 expression calls the overloaded + operator.
#include <iostream>
using namespace std;
class Complex {
public:
double real, imag;
Complex(double r, double i) : real(r), imag(i) {}
// Overload the + operator to add two complex numbers
Complex operator + (const Complex& other) {
return Complex(real + other.real, imag + other.imag);
}
// Method to display the complex number
void display() {
cout << real << " + " << imag << "i" << endl;
}
};
int main() {
Complex c1(3.0, 4.0), c2(1.0, 2.0);
// Add two complex numbers using overloaded + operator
Complex c3 = c1 + c2;
// Display the result
c3.display(); // Output: 4.0 + 6.0i
return 0;
}
Output:
4 + 6i
Method Overriding
Method overriding allows a derived class to provide a specific implementation of a method that is already defined in its base class.
Example:
The Animal class has a virtual method makeSound.
The Dog class overrides makeSound to provide a specific implementation.
A base class pointer animalPtr calls the overridden method, demonstrating runtime polymorphism.
#include <iostream>
using namespace std;
// Base class
class Animal {
public:
virtual void makeSound() {
cout << "Animal makes a sound" << endl;
}
};
// Derived class
class Dog : public Animal {
public:
void makeSound() override {
cout << "Dog barks" << endl; } }; int main() { Animal* animalPtr; Dog dog; // Pointing base class pointer to derived class object animalPtr = &dog; // Call the overridden method animalPtr->makeSound(); // Output: Dog barks
return 0;
}
Output:
Dog barks
Abstraction
Abstraction is an OOP concept that hides the complex implementation details and shows only the essential features of an object. This simplifies the user interaction with the object and makes the code more readable and maintainable. In C++, abstraction is achieved using abstract classes and interfaces.
Abstract Class
An abstract class is a class that cannot be instantiated and is designed to be subclassed. It often contains one or more pure virtual functions.
Example:
The Device class is an abstract class with pure virtual functions start and stop.
The Smartphone class inherits from Device and provides implementations for start and stop.
In the main function, we create an object of the Smartphone and call the implemented methods.
#include <iostream>
using namespace std;
// Abstract class
class Device {
public:
// Pure virtual function
virtual void start() = 0;
virtual void stop() = 0;
};
// Derived class
class Smartphone : public Device {
public:
void start() override {
cout << "Smartphone is starting" << endl;
}
void stop() override {
cout << "Smartphone is stopping" << endl;
}
};
int main() {
// Create an object of the derived class
Smartphone myPhone;
// Call the abstract class methods implemented by the derived class
myPhone.start(); // Output: Smartphone is starting
myPhone.stop(); // Output: Smartphone is stopping
return 0;
}
Output:
Smartphone is starting
Smartphone is stopping
This example demonstrates how abstraction allows you to define a template for future classes and ensure they implement specific methods, simplifying complex functionalities and improving code organisation.
Encapsulation
Encapsulation is an OOP concept that bundles the data (attributes) and methods (functions) that operate on the data into a single unit, called a class. It restricts direct access to some of an object’s components, which is a means of preventing accidental interference and misuse of the data. Encapsulation helps in protecting the internal state of an object and only allows manipulation through defined methods.
Example:
The Smartphone class encapsulates the properties brand and model as private members. This means they cannot be accessed directly from outside the class.
Public methods (setBrand, getBrand, setModel, and getModel) provide controlled access to modify and retrieve the private data.
The displayInfo method allows displaying the current state of the Smartphone object.
In the main function, we create an object of the Smartphone class, use the methods to modify its properties, and display the results.
#include <iostream>
using namespace std;
class Smartphone {
private:
string brand;
string model;
public:
// Constructor to initialise the brand and model
Smartphone(string b, string m) {
brand = b;
model = m;
}
// Method to set the brand
void setBrand(string b) {
brand = b;
}
// Method to get the brand
string getBrand() {
return brand;
}
// Method to set the model
void setModel(string m) {
model = m;
}
// Method to get the model
string getModel() {
return model;
}
// Method to display the smartphone details
void displayInfo() {
cout << "Brand: " << brand << ", Model: " << model << endl;
}
};
int main() {
// Create an object of the Smartphone class
Smartphone myPhone("Samsung", "Galaxy S21");
// Display the initial smartphone details
myPhone.displayInfo(); // Output: Brand: Samsung, Model: Galaxy S21
// Modify the smartphone details using setter methods
myPhone.setBrand("Apple");
myPhone.setModel("iPhone 12");
// Display the updated smartphone details
myPhone.displayInfo(); // Output: Brand: Apple, Model: iPhone 12
return 0;
}
Dynamic binding, also known as late binding, is a concept in OOP where the method to be called is determined at runtime. This is mainly used with polymorphism and allows for more flexible and extensible code. In C++, dynamic binding is achieved using virtual functions.
Example:
The Device class has a virtual function start.
The Smartphone and Laptop classes override the start function.
A pointer to the Device class can point to objects of Smartphone and Laptop.
The actual function called is determined at runtime based on the object being pointed to.
#include <iostream>
using namespace std;
// Base class
class Device {
public:
// Virtual function
virtual void start() {
cout << "Device is starting" << endl;
}
};
// Derived class
class Smartphone : public Device {
public:
void start() override {
cout << "Smartphone is starting" << endl;
}
};
// Another derived class
class Laptop : public Device {
public:
void start() override {
cout << "Laptop is starting" << endl; } }; int main() { Device* device; Smartphone phone; Laptop laptop; // Point to a Smartphone object device = ☎ device->start(); // Output: Smartphone is starting
// Point to a Laptop object
device = &laptop;
device->start(); // Output: Laptop is starting
return 0;
}
Output:
Smartphone is starting
Laptop is starting
Message Passing
Message passing in OOP refers to the process of objects communicating with each other by sending and receiving information. This is usually done through method calls, where one object invokes a method on another object.
Example:
The Processor class has a method processMessage that takes a string message.
The Smartphone class contains an object of the Processor class and a method sendMessage.
The sendMessage method in Smartphone calls the processMessage method of the Processor object, demonstrating message passing between objects.
In the main function, we create a Smartphone object and send a message, showing how the message is passed and processed.
#include <iostream>
using namespace std;
class Processor {
public:
void processMessage(string message) {
cout << "Processing message: " << message << endl;
}
};
class Smartphone {
private:
Processor processor;
public:
void sendMessage(string message) {
cout << "Sending message: " << message << endl;
// Pass the message to the Processor object for further action
processor.processMessage(message);
}
};
int main() {
// Create an object of the Smartphone class
Smartphone myPhone;
// Send a message using the Smartphone object
myPhone.sendMessage("Hello, World!"); // Output: Sending message: Hello, World!
// Processing message: Hello, World!
return 0;
}
Procedural Programming vs. Object-Oriented Programming
Feature
Procedural Programming
Object-Oriented Programming (OOP)
Approach
Top-down
Bottom-up
Basic Unit
Function
Class and Object
Data Handling
Global data shared across functions
Encapsulated data within objects
Code Reusability
Limited reusability, primarily through functions
High reusability through inheritance and polymorphism
Focus
Functions and sequences of actions
Objects and interactions between them
Modularity
Functions
Classes and Objects
Data Security
Low, data is accessible by any function
High, data is hidden and protected within objects
Maintenance
Harder to maintain in large projects
Easier to maintain due to modular structure
Examples
C, Pascal
C++, Java, Python
Conclusion
Object-Oriented Programming is all about managing code in C++. By learning how you can apply classes, objects, inheritance, polymorphism, abstraction, encapsulation, dynamic binding and message passing you will be able to create software that is modular which can also be reused and maintained too.
OOP’s ability to model real-world entities and relationships makes it a preferred choice for complex software development. Whether transitioning from procedural programming or starting from scratch mastering these concepts in C++ would significantly improve your coding skills as well as project outcomes.
FAQs
How does procedural programming differ from OOP?
An emphasis on functions and sequence of actions are the main features of procedural programming while focus on objects’ interactions lies at the core of OOP.
Why do I need inheritance?
Inheritance permits a class to acquire properties plus behaviours from other classes, thereby facilitating the reuse and extension of code.
What do you understand by polymorphism?
Different behaviours may be exhibited by methods depending on which object calls them, i.e., upon function overloading, operator overloading method overriding, etc. This mechanism is otherwise known as Polymorphism.
Why does encapsulation enhance the security of data?
Encapsulation limits direct access to an object’s information and thus possesses control measures through which information can only be interacted with through defined methods.
Can you explain dynamic binding using an example?
The method that is determined at run time is known as dynamic binding. In other words, a base class pointer that points to derived objects can call overridden methods based on the actual object type at runtime.
Hero Vired is a leading LearnTech company dedicated to offering cutting-edge programs in collaboration with top-tier global institutions. As part of the esteemed Hero Group, we are committed to revolutionizing the skill development landscape in India. Our programs, delivered by industry experts, are designed to empower professionals and students with the skills they need to thrive in today’s competitive job market.