Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Inheritance


Contents

Introduction



A class can be derived from from another class. Let's say we have a Person class. We can have an Employee class that is derived from it. The Employee class will inherit all the member data variables and the member functions from the Person class. Since an Employee class is derived from a Person class it is a Person class also. This is called upcasting. However a Person may not be an employee so we cannot downcast.
File: i1.cpp
#include <iostream>
using namespace std ;

class  Person
{
    public:
    string firstName  ;
    string lastName ;
    string getName()
    {
        return ( firstName + " " + lastName ) ;
    }
};

class Employee : public Person
{
    public:
     string jobTitle ;
     void print()
     {
         cout << getName() << " " << jobTitle << endl ;
     }
};

int main()
{
    Employee e1  ;
    //Can access the base class variables
    e1.firstName = "Alan"  ;
    e1.lastName = "Turing"  ;
    e1.jobTitle = "Programmer" ;
    e1.print() ;
    Person p1 ;
    p1 = e1 ;
    //Can't do
    // e1 = p1 ;
    return 0 ;
}
In the above example we have a base class called "Person" and a derived class "Employee". An employee is a person but has more attributes; in this case "jobTitle". It can inherit the attributes and the member functions from the base class.
There is a new access specifier called "protected" . A protected data member can be seen by the derived class but not outside the derived class.
File: i2.cpp
class A1
{
    public:
     int x1 ;
    protected :
     int x2 ;
    private :
     int x3 ;
};

//B1 is derived from A1class

B1 : public A1
{
    void method1()
    {
        x2 = 4 ;
        //A derived class can access
        //a protected member
        //x3 = 5 ;
        //Cannot access a private member of a base class
    }
};

int main()
{
    A1 A1Object ;
    B1 B1Object ;
    int c1 = A1Object.x1 ;
    //c1 = A1Object.x2 ;
    //Access issue
    return 0 ;
}
The "protected" scope is not visible outside the class but is visible to the derived class.

Exercise

1)Modify the firstName and lastName in the Person example above to be protected and "getName" to be public. Comment out the below 2 lines.
e1.firstName = "Alan" ; e1.lastName = "Turing" ;
Compile and run the program.
File: ex1.cpp
In addition to data members having access specifier we can specify an access specifier in the derived class and that tells us what the access ( modified ) would be in the derived class for the base class for someone looking at it from outside. One easy way to remember what the access means is to think of the access as it applies to the public and protected members of the base class. Public inheritance means that the public and protected members of the base class stay as it is. Protected inheritance means that the public and protected members of the base class become protected and private inheritance means the public and protected members of the base class become private.
The concept is somewhat confusing in the beginning and we need to look at some examples.

Public Inheritance


File: i3.cpp
class Base
{
    public:
    int m_public;
    private:
    int m_private;
    protected:
    int m_protected;
};

class Pub: public Base
// note: public inheritance
{
    // Public inheritance means:
    // Public inherited members stay public
    //(so m_public is treated as public)

    // Protected inherited members stay protected
    //(so m_protected is treated as protected)
    // Private inherited members stay inaccessible
    //(so m_private is inaccessible)
    public:
    Pub()
    {
        m_public = 1;
        // okay: m_public was inherited as public
        //m_private = 2;
        // not okay: m_private is inaccessible from derived class
        m_protected = 3;
        // okay: m_protected was inherited as protected
    }
};
int main()
{
    // Outside access uses the access specifiers of the
    //class being accessed.
    Base base;
    base.m_public = 1;
    // okay: m_public is public in Base
    //base.m_private = 2;
    // not okay: m_private is private in Base
    //base.m_protected = 3;
    // not okay: m_protected is protected in Base
    Pub pub;
    pub.m_public = 1;
    // okay: m_public is public in Pub
    //pub.m_private = 2;
    // not okay: m_private is inaccessible in Pub
    //pub.m_protected = 3;
    // not okay: m_protected is inaccessible in Pub
}

Protected Inheritance


File: i4.cpp
class Base
{
    public:
     int m_public;
    private:
     int m_private;
    protected:
     int m_protected;
};

class Protect: protected Base
{
    // Protected inheritance means:
    // Public inherited members become protected
    //(so m_public is treated as protected)
    // Protected inherited members become protected
    //(so m_protected is treated as protected)
    // Private inherited members stay inaccessible
    //(so m_private is inaccessible)public:
    Protect()
    {
        m_public = 1;
        // okay: m_public is now protected in Protect
        //m_private = 2;
        // not okay: derived classes can't access private members
        //in the base class
        m_protected = 3;
        // okay: m_protected is protected in Protect
    }
};
class Derived : public Protect
{
    public:
     Derived()
     {
         m_protected = 5 ;
     }
};
int main()
{
    // Outside access uses the access specifiers of the class
    //being accessed.
    // In this case, the access specifiers of base.
    Base base;
    base.m_public = 1;
    // okay: m_public is public in Base
    //base.m_private = 2;
    // not okay: m_private is private in Base
    //base.m_protected = 3;
    // not okay: m_protected is protected in Base
    Protect pri;
    //pri.m_public = 1;
    // not okay: m_public is now protected in Protect
    //pri.m_private = 2;
    // not okay: m_private is inaccessible in Protect
    //pri.m_protected = 3;
    // not okay: m_protected is now protected in Protect
}

Private Inheritance


File: i5.cpp
class Base
{
    public:
    int m_public;
    private:
    int m_private;
    protected:
    int m_protected;
};

class Pri: private Base
    // note: private inheritance
{
        // Private inheritance means:
        // Public inherited members become private
        //(so m_public is treated as private)
        // Protected inherited members become private
        //(so m_protected is treated as private)
        // Private inherited members stay inaccessible
        //(so m_private is inaccessible)
        public:
        Pri()
        {
            m_public = 1;
            // okay: m_public is now private in Pri
            // m_private = 2;
            // not okay: derived classes can't access
            //private members in the base class
            m_protected = 3;
            // okay: m_protected is now private in Pri
        }
};

class Derived : public Pri
{
   public:
   Derived()
    {
        //Compiler error
        //m_protected = 5 ;
    }
};


int main()
{
    // Outside access uses the access specifiers of the class being accessed.
    // In this case, the access specifiers of base.
    Base base;
    base.m_public = 1;
    // okay: m_public is public in Base
    //base.m_private = 2;
    // not okay: m_private is private in Base
    //base.m_protected = 3;
    // not okay: m_protected is protected in Base
    Pri pri;
    //   pri.m_public = 1;
    // not okay: m_public is now private in Pri
    //pri.m_private = 2;
    // not okay: m_private is inaccessible in Pri
    //pri.m_protected = 3;
    // not okay: m_protected is now private in Pri
}


Exercise

Find all the access errors in the below program.
File: ex2.cpp
class Base
{
    public:
    int m_public;
    Base()
    {
        m_private = 3 ;
        }
        private:
        int m_private ;
        protected:
        int m_protected ;
        };
class Derived: public Base
{
  protected:
    int p_var1 ;
  private:
    int p_var2 ;
  public:
    int p_var3 ;
    Derived()
    {
        m_private = 4 ;
        m_protected = 5 ;
        p_var2 = 6 ;
    }
};

class Derived1 : private Derived
{
  public:
    Derived1()
    {
        p_var1 = 4 ;
        p_var2 = 5 ;
        m_public = 6 ;
    }
};

int main()
{
        Base baseObject ;
        Derived derivedObject ;
        Derived1 derived1Object ;
        baseObject.m_public = 1;
        baseObject.m_protected = 2 ;
        derivedObject.m_public = 1 ;
        derived1Object.m_public = 1 ;
        derived1Object.p_var3 = 3 ;
}

Calling the base class constructor

If the base class has a constructor then how can we call it when constructing an object of the derived class ? We can use initialization lists.
File: base1.cpp
#include <iostream>
using namespace std ;

class  Person
{
    public:
     string firstName  ;
     string lastName ;
     string getName()
     {
         return ( firstName + " " + lastName ) ;
         }
         Person( string firstNameP, string lastNameP )
         {
             firstName = firstNameP ;
             lastName = lastNameP ;
             }
             };

class Employee : public Person
{
  public:
    string jobTitle ;
    Employee( string firstNameP, string lastNameP, string jobTitleP)
                 :Person(firstNameP, lastNameP  )
         {
                 jobTitle = jobTitleP ;
         }
     void print()
     {
             cout << getName() << " " << jobTitle << endl ;
     }
};


int main()
{
  Employee e1( "Alan" , "Turing" , "Programmer" ) ;
  e1.print() ;
  return 0 ;
}


We can only call the immediate base ( parent class constructor ).There is a caveat to this in multiple inheritance.
File: base2.cpp
$ g++ base2.cpp
base2.cpp: In constructor ‘Manager::Manager(std::string)’:
base2.cpp:64:47: error: type ‘Person’ is not a direct base of ‘Manager’
   64 |                 Manager( string firstNameP ): Person( firstNameP ) , Employee( firstNameP )

If we have same name variables in derived and base classes then we can refer to the base class variable name using the syntax "base::variable" .
File: base3.cpp
#include <iostream>
using namespace std ;
//Virtual Destructor
class  Person
{
    public:
    string firstName  ;
    string lastName ;
    //virtual
    void getName()
    {
        cout <<  firstName << " " <<  lastName << endl ;
    }
    Person( string firstNameP )
    {
       firstName = firstNameP ;
      cout << "Person Constructor." << endl ;
    }

    //virtual
    ~Person()
    {
      cout << "Person Destructor." << endl ;
    }

};

class Employee : public Person
{
    public:
     string jobTitle ;
     virtual
     void getName()
     {
         cout <<  firstName << " " <<  lastName <<
         " " << jobTitle << endl ;

     }

          Employee(string firstNameP) : Person( firstNameP )
          {
          jobTitle = "Employee title." ;
            cout << "Employee Constructor." << endl ;
          }

         //virtual
         ~Employee()
         {
           cout << "Employee Destructor." << endl ;
         }

};

class Manager : public Employee
{
    public:
     //What if the method is missing
     //Climb up the hierarchy
     string jobTitle ;
     virtual void getName()
     {
         cout <<  firstName << " " <<  lastName <<
         " " << jobTitle << Employee::jobTitle <<  " Manager" << endl ;

     }
          Manager( string firstNameP ):  Employee( firstNameP )
          {
          jobTitle = "Manager title." ;
            cout << "Manager Constructor." << endl ;
          }

         //virtual
         ~Manager()
         {
           cout << "Manager Destructor." << endl ;
         }
};


int main()
{
    Person* p1 ;
    Employee* e1  ;
    Manager* m1  ;
    m1 = new Manager( "Chuck" ) ;
    m1->firstName = "Chuck" ;
    m1->lastName = "Wepner" ;
    m1->jobTitle = "Boxer" ;
    m1->getName() ;
    //p1 = m1 ;
    //e1->getName() ;
    //Make sure we delete the right type
    //or program will crash
    delete m1 ;

    return 0 ;
}


Exercise

2)Modify the below program so that the constant id in the class Person is set by modifying the constructors of both the Person and Employee class.


File: ex2.cpp
#include <iostream>
using namespace std ;

class  Person
{
    public:
    string firstName  ;
    string lastName ;
    const int id ;
    string getName()
    {
        return ( firstName + " " + lastName ) ;
    }
    Person( string firstNameP, string lastNameP )
    {
            firstName = firstNameP ;
            lastName = lastNameP ;
    }
};

class Employee : public Person
{
   public:
      string jobTitle ;
      Employee( string firstNameP, string lastNameP, string jobTitleP)
                :Person(firstNameP, lastNameP  )
      {
        jobTitle = jobTitleP ;
      }
    void print()
    {
        cout << getName() << " " << jobTitle << " " << id << endl ;
    }
};


int main()
{
    Employee e1( "Alan" , "Turing" , "Programmer" , 1) ;
    e1.print() ;
    return 0 ;
}

Polymorphism

The word means to take on many forms. If we have a base class and a derived class that have methods of the same name: say a base class "Person" and a derived class called "Employee". We know that an "Employee" is a "Person" so we can upcast a pointer of "Employee" to "Person" but now if we call the common method "getName" what class method should be called; the "Person" or the "Employee".
Polymorphism allows the right ( original ) class method to be called.
File: v1.cpp

The poymorphism does not work with objects but only pointers and references.
1) Virtual functions cannot be static.
2) A virtual function can be a friend function of another class.
3) Virtual functions should be accessed using a pointer or reference
of base class type to achieve runtime polymorphism.
4) The prototype of virtual functions should be the same in the base
as well as the derived class.
5) They are always defined in the base class and overridden in a
derived class. It is not mandatory for the derived class to override
(or re-define the virtual function), in that case, the base class version
of the function is used.
6) A class may have a virtual destructor but it cannot have a
virtual constructor.
Virtual methods are implemented by virtual method tables. Each class will keep a virtual method table keeping pointers to the functions. https://pabloariasal.github.io/2017/06/10/understanding-virtual-tables/ If we don't want Polymorphism then we don't use the "virtual" keyword. This saves us resources. Polymorphism begins in the hierarchy when the virtual keyword is declared.
If we have say three classes

A
  method M1
B
 virtual method M1

C
 virtual method M1

Then if we create an object of type C and upcast to A then we will not
have polymorphism. However if we type cast to B then we will have polymorphism.
Also once we write the virtual keyword then we do not need to write the
virtual keyword in the dervice classes methods.

File: v2.cpp
If we have the actual object but that object does not have the virtual method defined then we climb up the hierarchy till we find a method definition.
File: v3.cpp

Virtual Destructor

Anytime we have destructors in our hierarchy and use polymorphism we should make the destructors virtual. That way when we have a pointer to the base class and we do a delete then the derived destructor also gets called.
File: v4.cpp
Output is:
Person Constructor.
Employee Constructor.
Employee Destructor.
Person Destructor.


Person Constructor.
Employee Constructor.
Manager Constructor.
Manager Destructor.
Employee Destructor.
Person Destructor.

If we don't have the destructor as virtual then we get the following results. We may get the right results or not. It is always better to use the virtual keyword for destructors.
File: v5.cpp
Output is:
Person Constructor.
Employee Constructor.
Employee Destructor.
Person Destructor.


Person Constructor.
Employee Constructor.
Manager Constructor.
Manager Destructor.
Employee Destructor.
Person Destructor.

We can have a private virtual method in the base calss and it can only be called by the base class method but polymorphism will still occur.
File: v6.cpp

Abstract Class

Sometimes we don't want an instance of a class to be created. We can have a pure virtual function in the class without a body in that case.
File: a1.cpp
If the pure virtual function is not implemented in the derived class then the derived class becomes abstract also.
File: a2.cpp
Using pure and normal virtual functions a userful hierarchy can be implemented where the base class and the derived class work together. In the below program we have a "Vector" object that is able to call a base class method called "printElements" that in turn is able to call the derived class methods even though it has no idea of what the implmentations are at the time the class was written. In fact tomorrow if a new derived class from "Collection" then the method "printElements" will still work.
File: a3.cpp

Multiple Inheritance

We can have a class derive from 2 classes.
File: multi1.cpp
The diamond problem:
     class A
  class B     class C
         class D

class D inherits 2 copies of class A

File: multi2.cpp
Use the "virtual" on class to prevent 2 copies.
File: multi3.cpp
Calling the base class constructor when it has argument
File: multi4.cpp
Only works for virtual derived classes.
File: multi5.cpp


Exercises:

File: ex4.cpp

File: ex5.cpp