Blog header background

OOPs Concepts in C++ Explained with Examples

Updated on October 10, 2024

18 min read

Copy link
Share on WhatsApp

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.
brochure-banner-bg

POSTGRADUATE PROGRAM IN

Multi Cloud Architecture & DevOps

Master cloud architecture, DevOps practices, and automation to build scalable, resilient systems.

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


Output:

    Brand: Samsung, Model: Galaxy S21

    Brand: Apple, Model: iPhone 12

Dynamic Binding

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


Output:

    Sending message: Hello, World!

    Processing message: Hello, World!    
    

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.

Updated on October 10, 2024

Link
Loading related articles...