Inheritance In C++

Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (derived class) to inherit properties and behaviors from another class (base class). In C++, inheritance can be categorized into three types based on the access level of the inherited members: public, private, and protected inheritance.

Access Modifiers

In C++, access modifiers are keywords used to specify the accessibility of class members (variables and functions) from outside the class. There are three access modifiers in C++: public, private, and protected.

  • Public Access Modifier - When a class member is declared as public, it can be accessed from outside the class through objects of that class. Public members are accessible from anywhere in the program, including outside the class and its derived classes.

  • Private Access Modifier - When a class member is declared as private, it can only be accessed by other members of the same class.

  • Protected Access Modifier - When a class member is declared as protected, it can be accessed by other members of the same class and its derived classes.

Example Code:

#include <iostream>
using namespace std;
 
class MyClass {
public:
    // Public member
    int publicMember;
 
    // Public member function
    void publicFunction() {
        cout << "Public Function" << endl;
    }
 
private:
    // Private member
    int privateMember;
 
    // Private member function
    void privateFunction() {
        cout << "Private Function" << endl;
    }
 
protected:
    // Protected member
    int protectedMember;
 
    // Protected member function
    void protectedFunction() {
        cout << "Protected Function" << endl;
    }
};
 
int main() {
    MyClass obj;
 
    // Accessing public members
    obj.publicMember = 10;
    obj.publicFunction();
 
    // Private members are not accessible outside the class
    // obj.privateMember = 20; // Error: privateMember is inaccessible
    // obj.privateFunction(); // Error: privateFunction() is inaccessible
 
    // Protected members are not accessible outside the class
    // obj.protectedMember = 30; // Error: protectedMember is inaccessible
    // obj.protectedFunction(); // Error: protectedFunction() is inaccessible
 
    return 0;
}

Output:

Public Function

In this example:

  • The publicMember and publicFunction() are accessible from outside the class.
  • The privateMember and privateFunction() are private members and are not accessible from outside the class.
  • The protectedMember and protectedFunction() are protected members and are not accessible from outside the class but can be accessed by derived classes.

Derived Class Declaration

A derived class is declared by specifying the base class from which it inherits using a colon : followed by the access specifier (public, private, or protected) and the base class name. Here's the general syntax:

class DerivedClass : accessSpecifier BaseClass {
    // Class members and methods
};

Public Inheritance

In public inheritance, all public members of the base class become public members of the derived class, and all protected members of the base class become protected members of the derived class. However, the private members of the base class remain inaccessible in the derived class.

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};
 
class Derived : public Base {
    // publicMember and protectedMember are accessible here
    // privateMember is not accessible here
};

Private Inheritance

In private inheritance, all public and protected members of the base class become private members of the derived class. This means that they are not accessible outside the derived class.

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};
 
class Derived : private Base {
    // publicMember and protectedMember are private members of Derived
    // privateMember is not accessible here
};

Protected Inheritance

In protected inheritance, all public and protected members of the base class become protected members of the derived class. This means that they are accessible within the derived class and its derived classes but not outside of them.

class Base {
public:
    int publicMember;
protected:
    int protectedMember;
private:
    int privateMember;
};
 
class Derived : protected Base {
    // publicMember and protectedMember are protected members of Derived
    // privateMember is not accessible here
};

Example Code:

Here's an example demonstrating inheritance in C++:

#include <iostream>
using namespace std;
 
// Base class
class Base {
public:
    int x = 7;
protected:
    int y = 5;
private:
    int privateMember;
};
 
// Derived class using public inheritance
class DerivedPublic : public Base {
public:
 
    //DerivedPublic Constructor
    DerivedPublic(){
        cout<<"DerivedPublic Invoked"<<endl;
    }
 
    void display() {
        cout << "DerivedPublic: x = " << x << endl;
        cout << "DerivedPublic: y = " << y << endl;
        // privateMember is not accessible here
    }
};
 
// Derived class using private inheritance
class DerivedPrivate : private Base {
public:
    
    //DerivedPrivate Constructor
    DerivedPrivate(){
        cout<<"DerivedPrivate Invoked"<<endl;
    }
    
    void display() {
        cout << "DerivedPrivate: x = " << x << endl;
        cout << "DerivedPrivate: y = " << y << endl;
        // privateMember is not accessible here
    }
};
 
// Derived class using protected inheritance
class DerivedProtected : protected Base {
public:
 
    //DerivedProtected Constructor
    DerivedProtected(){
        cout<<"DerivedProtected Invoked"<<endl;
    }
    
    void display() {
        cout << "DerivedProtected: x = " << x << endl;
        cout << "DerivedProtected: y = " << y << endl;
        // privateMember is not accessible here
    }
};
 
int main() {
    DerivedPublic derivedPublic;
    derivedPublic.display();
 
    DerivedPrivate derivedPrivate;
    // derivedPrivate.display(); // Error: display() is inaccessible
 
    DerivedProtected derivedProtected;
    // derivedProtected.display(); // Error: display() is inaccessible
 
    return 0;
}

Output:

DerivedPublic Invoked 
DerivedPublic: x = 7
DerivedPublic: y = 5
DerivedPrivate Invoked
DerivedProtected Invoked

Explanation:

  1. For DerivedPublic class (public inheritance):

    • DerivedPublic class inherits publicly from Base class. Therefore, both x and y of the Base class are accessible in the DerivedPublic class.
    • The DerivedPublic class constructor is invoked when an object of DerivedPublic class is created.
    • The display() function of DerivedPublic class prints the values of x and y.
  2. For DerivedPrivate class (private inheritance):

    • DerivedPrivate class inherits privately from Base class. This means that all members of Base class become private members of DerivedPrivate class, making them inaccessible outside DerivedPrivate class.
    • The DerivedPrivate class constructor is invoked when an object of DerivedPrivate class is created.
    • Attempting to call the display() function results in an error because it's inaccessible outside DerivedPrivate.
  3. For DerivedProtected class (protected inheritance):

    • DerivedProtected class inherits protectedly from Base class, making x and y protected members of DerivedProtected class.
    • The DerivedProtected class constructor is invoked when an object of DerivedProtected class is created.
    • Similar to DerivedPrivate class, the display() function is inaccessible outside DerivedProtected class.

Friend Function

In C++, a friend function is a function that is not a member of a class but has access to the class's private and protected members. When a function is declared as a friend of a class, it can access private and protected members of that class as if it were a member function of that class. This provides flexibility in defining functions that need access to private or protected members without being members of the class itself.

A friend function of a base class does not have direct access to members of a derived class, even if those members are accessible to the base class. To access members of a derived class, the friend function needs to be declared as a friend in the derived class as well.

Base Class With Friend Function:

#include <iostream>
using namespace std;
 
// Base class
class Base {
private:
    int x = 10;
protected:
    int y = 20;
public:
    friend void baseFriendFunction(const Base&); // Declaration of friend function
};
 
// Friend function definition
void baseFriendFunction(const Base& obj) {
    cout << "Accessing x from baseFriendFunction: " << obj.x << endl;
    cout << "Accessing y from baseFriendFunction: " << obj.y << endl;
}
 
int main() {
    Base baseObj;
    baseFriendFunction(baseObj); // Accessing Base class private and protected members
    return 0;
}

Output:

Accessing x from baseFriendFunction: 10
Accessing y from baseFriendFunction: 20

Explanation:

  • In this code, we have the Base class with private and protected members.
  • We define a friend function baseFriendFunction that takes a const Base& reference as a parameter to access private and protected members of the Base class.
  • Inside the main() function, we create an object baseObj of the Base class.
  • We then call the baseFriendFunction with baseObj as an argument.
  • The baseFriendFunction function accesses the private and protected members of baseObj, printing their values.

Derived Class with Friend Function:

#include <iostream>
using namespace std;
 
// Base class
class Base {
private:
    int x = 10;
protected:
    int y = 20;
};
 
// Derived class
class Derived : public Base {
private:
    int p = 30;
protected:
    int q = 40;
public:
    friend void derivedFriendFunction(const Derived&); // Declaration of friend function
};
 
// Friend function definition for Derived class
void derivedFriendFunction(const Derived& obj) {
    cout << "Accessing p from derivedFriendFunction: " << obj.p << endl;
    cout << "Accessing q from derivedFriendFunction: " << obj.q << endl;
}
 
int main() {
    Derived derivedObj;
    derivedFriendFunction(derivedObj); // Accessing Derived class private and protected members
    return 0;
}

Output:

Accessing p from derivedFriendFunction: 30
Accessing q from derivedFriendFunction: 40

Explanation:

  • In this code, we have two classes: Base and Derived, where Derived publicly inherits from Base.
  • We define a friend function derivedFriendFunction to access private and protected members of the Derived class.
  • Inside the main() function, we create an object derivedObj of the Derived class.
  • We then call the derivedFriendFunction with derivedObj as an argument.
  • The derivedFriendFunction function accesses the private and protected members of the Derived class, printing their values.

Friend Classes

Friend classes in C++ provide a way to grant access to the private and protected members of one class to another class. When a class is declared as a friend of another class, it can access private and protected members of that class, just like the members of the class itself. This feature is often used when two or more classes need to share data or functionality that is not intended to be publicly accessible.

Here's a brief overview of friend classes:

  • Access to Private and Protected Members: A friend class has access to all the private and protected members of the class it is declared as a friend of.
  • Declaration Syntax: To declare a friend class, use the friend keyword followed by the class declaration inside the class that is granting friendship.
  • Bidirectional Friendship: Friendship is not symmetric. If class A is a friend of class B, it doesn't imply that class B is automatically a friend of class A. Friendship must be explicitly declared in both directions if needed.
#include <iostream>
using namespace std;
 
class A {
private:
    int privateDataA;
 
public:
    
    A(){
        privateDataA = 5;
    }
 
    friend class B; // Declaring class B as a friend of class A
};
 
class B {
public:
    void accessPrivateDataA(A &objA) {
        cout << "Accessing privateDataA from class B: " << objA.privateDataA << endl;
    }
};
 
int main() {
    A objA;
    B objB;
    
    objB.accessPrivateDataA(objA); // Class B accessing private data of class A
 
    return 0;
}

Output:

Accessing privateDataA from class B: 5

In this example:

  • class B is declared as a friend of class A using the friend keyword.
  • class B can now access the private member privateDataA of class A.
  • The accessPrivateDataA method in class B accesses the private data of class A through an object of class A.

Forms Of Inheritance

Inheritance allows a class to inherit properties and behaviors (methods) from another class. There are different forms of inheritance in C++:

Single Inheritance

Single Inheritance

A derived class inherits from only one base class.

#include <iostream>
using namespace std;
 
// Base class
class Base {
public:
    void display() {
        cout << "Base class display function" << endl;
    }
};
 
// Derived class inheriting from Base
class Derived : public Base {
public:
    void show() {
        cout << "Derived class show function" << endl;
    }
};
 
int main() {
    Derived derivedObj;
    derivedObj.display(); // Accessing Base class function
    derivedObj.show();    // Accessing Derived class function
    return 0;
}

Output:

Base class display function
Derived class show function

Explanation:

  • In this code, Derived class inherits publicly from Base.
  • The output displays the function calls to both the base class function display() and the derived class function show().

Multiple Inheritance

Multiple Inheritance

A derived class inherits from more than one base class.

#include <iostream>
using namespace std;
 
// Base class 1
class Base1 {
public:
    void display1() {
        cout << "Base1 class display function" << endl;
    }
};
 
// Base class 2
class Base2 {
public:
    void display2() {
        cout << "Base2 class display function" << endl;
    }
};
 
// Derived class inheriting from Base1 and Base2
class Derived : public Base1, public Base2 {
public:
    void show() {
        cout << "Derived class show function" << endl;
    }
};
 
int main() {
    Derived derivedObj;
    derivedObj.display1(); // Accessing Base1 class function
    derivedObj.display2(); // Accessing Base2 class function
    derivedObj.show();      // Accessing Derived class function
    return 0;
}

Output:

Base1 class display function
Base2 class display function
Derived class show function

Explanation:

  • In this code, Derived class inherits from both Base1 and Base2.
  • The output displays the function calls to each base class function display1() and display2() and the derived class function show().

Multilevel Inheritance

Multilevel Inheritance

A derived class inherits from another derived class, creating a hierarchy of classes.

#include <iostream>
using namespace std;
 
// Base class
class Base {
public:
    void display() {
        cout << "Base class display function" << endl;
    }
};
 
// Derived class inheriting from Base
class Derived : public Base {
public:
    void show() {
        cout << "Derived class show function" << endl;
    }
};
 
// Another derived class inheriting from Derived
class AnotherDerived : public Derived {
public:
    void print() {
        cout << "AnotherDerived class print function" << endl;
    }
};
 
int main() {
    AnotherDerived derivedObj;
    derivedObj.display(); // Accessing Base class function
    derivedObj.show();    // Accessing Derived class function
    derivedObj.print();   // Accessing AnotherDerived class function
    return 0;
}

Output:

Base class display function
Derived class show function
AnotherDerived class print function

Explanation:

  • In this code, AnotherDerived class inherits from Derived, which in turn inherits from Base.
  • The output displays the function calls to each class function display(), show(), and print(), demonstrating the multilevel inheritance hierarchy.

Hierarchical Inheritance

Hierarchical Inheritance

Multiple derived classes inherit from a single base class.

#include <iostream>
using namespace std;
 
// Base class
class Base {
public:
    void display() {
        cout << "Base class display function" << endl;
    }
};
 
// Derived class 1 inheriting from Base
class Derived1 : public Base {
public:
    void show1() {
        cout << "Derived1 class show1 function" << endl;
    }
};
 
// Derived class 2 inheriting from Base
class Derived2 : public Base {
public:
    void show2() {
        cout << "Derived2 class show2 function" << endl;
    }
};
 
int main() {
    Derived1 derived1Obj;
    Derived2 derived2Obj;
    derived1Obj.display(); // Accessing Base class function through Derived1 object
    derived2Obj.display(); // Accessing Base class function through Derived2 object
    derived1Obj.show1();   // Accessing Derived1 class function
    derived2Obj.show2();   // Accessing Derived2 class function
    return 0;
}

Output:

Base class display function
Base class display function
Derived1 class show1 function
Derived2 class show2 function

Explanation:

  • In this code, both Derived1 and Derived2 classes inherit from Base.
  • The output displays the function calls to each class function display(), show1(), and show2(), demonstrating the hierarchical inheritance where multiple derived classes inherit from a single base class.

Hybrid Inheritance

Hybrid Inheritance

A combination of two or more types of inheritance.

#include <iostream>
using namespace std;
 
// Base class
class Base {
public:
    void display() {
        cout << "Base class display function" << endl;
    }
};
 
// Derived class 1 inheriting from Base
class Derived1 : public Base {
public:
    void show1() {
        cout << "Derived1 class show1 function" << endl;
    }
};
 
// Derived class 2 inheriting from Base
class Derived2 : public Base {
public:
    void show2() {
        cout << "Derived2 class show2 function" << endl;
    }
};
 
// Derived class 3 inheriting from Derived1 and Derived2
class Derived3 : public Derived1, public Derived2 {
public:
    void print() {
        cout << "Derived3 class print function" << endl;
    }
};
 
int main() {
    Derived3 derivedObj;
    derivedObj.display(); // Accessing Base class function through Derived3 object
    derivedObj.show1();   // Accessing Derived1 class function
    derivedObj.show2();   // Accessing Derived2 class function
    derivedObj.print();   // Accessing Derived3 class function
    return 0;
}

Output:

Base class display function
Derived1 class show1 function
Derived2 class show2 function
Derived3 class print function

Explanation:

  • In this code, Derived3 class inherits from both Derived1 and Derived2, which in turn inherit from Base.
  • The output displays the function calls to each class function display(), show1(), show2(), and print(), demonstrating hybrid inheritance where multiple inheritance forms are combined.

Virtual Base Class

Multiple inheritance occurs when a class inherits from more than one base class. The diamond problem specifically arises in situations where a class inherits from two or more classes that have a common ancestor. This can lead to ambiguity in the inheritance hierarchy because the derived class indirectly inherits from the common ancestor through multiple paths. This ambiguity can cause issues during compilation or lead to unexpected behavior in the program.

#include <iostream>
using namespace std;
 
// Define a base class
class Base {
public:
    void display() {
        cout << "Base class\n";
    }
};
 
// Define two classes, Derived1 and Derived2, both inheriting from Base
class Derived1 : public Base {
public:
    // Additional members and methods...
};
 
class Derived2 : public Base {
public:
    // Additional members and methods...
};
 
// Define a class, MultipleDerived, inheriting from both Derived1 and Derived2
class MultipleDerived : public Derived1, public Derived2 {
public:
    // Additional members and methods...
};
 
int main() {
    MultipleDerived obj;
 
    obj.display(); // Error: ambiguous call to 'display'
    return 0;
}

Output:

Error: ambiguous call to 'display'

In this code:

  • We have a base class Base with a simple method display.
  • Two classes Derived1 and Derived2 inherit from Base.
  • Another class MultipleDerived inherits from both Derived1 and Derived2.
  • The MultipleDerived class now has two instances of the Base class due to the multiple inheritance paths.
  • When we try to call display directly on an object of MultipleDerived, it results in a compilation error due to ambiguity. Both Derived1 and Derived2 provide their own version of Base, leading to confusion.

To resolve this ambiguity, we can use virtual inheritance.

Virtual base classes provide a solution to the diamond problem by ensuring that there's only one instance of the common ancestor class shared among the derived classes. When a class is inherited virtually, it means that the derived classes share a single instance of the virtual base class, rather than each having their own separate instance. This sharing of the base class instance prevents ambiguity and ensures that each derived class has access to a single, consistent instance of the shared base class.

#include <iostream>
using namespace std;
 
// Define a base class
class Base {
public:
    void display() {
        cout << "Base class\n";
    }
};
 
// Define the classes with virtual inheritance
class Derived1 : virtual public Base {
    // Additional members and methods...
};
 
class Derived2 : virtual public Base {
    // Additional members and methods...
};
 
// Define a class, MultipleDerived, inheriting from both Derived1 and Derived2
class MultipleDerived : public Derived1, public Derived2 {
    // Additional members and methods...
};
 
int main() {
    MultipleDerived obj;
 
    obj.display(); // No ambiguity now, calls display from Base
 
    return 0;
}

Ouput:

Base class

In this code:

  • Derived1 and Derived2 both inherit virtually from Base.
  • MultipleDerived inherits from both Derived1 and Derived2.
  • Due to virtual inheritance, there's only one shared instance of Base among all classes in the inheritance hierarchy.
  • Now, calling display() on an object of MultipleDerived will correctly call the display() method from the shared Base class, avoiding ambiguity.

Abstract Class

An abstract class in C++ is a class that cannot be instantiated on its own. It serves as a blueprint for other classes and typically contains one or more pure virtual functions. A pure virtual function is a function declared in the base class that has no implementation and must be overridden by derived classes.

#include <iostream>
using namespace std;
 
// Abstract base class
class Shape {
public:
    virtual void draw() = 0;
};
 
// Concrete derived class Rectangle
class Rectangle : public Shape {
public:
    void draw() {
        cout << "Drawing rectangle..." << endl;
    }
};
 
// Concrete derived class Circle
class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing circle..." << endl;
    }
};
 
int main() {
    Rectangle rec;
    Circle cir;
 
    rec.draw();
    cir.draw();
 
    return 0;
}

Output:

Drawing rectangle...
Drawing circle...

Explanation:

  • The code defines an abstract base class Shape with a pure virtual function draw().
  • The draw() function in the Shape class has no implementation (pure virtual function), indicating that any derived class must override it.
  • Two concrete derived classes Rectangle and Circle inherit from the Shape class.
  • Both Rectangle and Circle classes override the draw() function according to their respective shapes.
  • In the main() function, objects of Rectangle and Circle are created.
  • When draw() is called on the rec object (of type Rectangle), it invokes the draw() function specific to rectangles, printing "Drawing rectangle...".
  • Similarly, when draw() is called on the cir object (of type Circle), it invokes the draw() function specific to circles, printing "Drawing circle...".

Advantage and disadvantage of Inheritance

Advantages

  • Code Reusability: Inheritance allows classes to inherit attributes and methods from other classes, promoting code reusability and reducing redundancy.
  • Extensibility: It enables the creation of new classes that are built upon existing classes, allowing for easy extension and modification of functionality without modifying the existing code.
  • Polymorphism: Inheritance facilitates polymorphic behavior, where objects of derived classes can be treated as objects of their base class, leading to flexibility in programming and improved code organization.
  • Modularity: It promotes a modular approach to software design by breaking down complex systems into smaller, manageable components, making the codebase easier to understand and maintain.

Disadvantages

  • Complexity: Inheritance hierarchies can become complex and difficult to understand, especially in large projects, leading to maintenance challenges and potential bugs.
  • Tight Coupling: Excessive use of inheritance can lead to tight coupling between classes, making the codebase less flexible and more difficult to refactor.
  • Inheritance Pollution: Subclasses inherit all members (both data and methods) of their base class, which can lead to inheritance pollution and potential conflicts if not managed properly.
  • Inflexibility: Changes to the base class can have unintended consequences on derived classes, potentially requiring modifications to multiple parts of the codebase, which can be time-consuming and error-prone.
How's article quality?

Last updated on -

Page Contents