Inheritance

Inheritance allows one class to incorporate another class into its declaration. In a situation where two or more classes might have similar attributes, inheritance allows this sharing of common hierarchical functionality. This is usually achieved by creating a base class containing all the common attributes and then allowing the derived class to inherit these shared attributes and define its unique attributes.

When defining a new derived class a colon is placed after the derived class name followed by the level of access ( public, protected, or private ) and then the name of the base class.

In the worked example below a class derived is derived from the base class company. Instantiating the derived class results in the instantiation of the base class -

#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base Constructer" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived Constructor" << endl;
}
};

int main() {
Derived dobj;
return 0;
}

Public, Protected and Private Inheritance

When one class inherits another, the base class members become members of the derived class. These base class members can only be accessed from within the derived class if the access specifier allows them to be inherited from the base class. The type of inheritance is specified by the access specifier which may be public, protected, or private

  • public - members are accessible from outside the class
  • private - members cannot be accessed (or viewed) from outside the class
  • protected - members cannot be accessed from outside the class, however, they can be accessed in inherited classes. 

Multiple Inheritance

A C++ class can inherit members from more than one class with each base class being separated in the derived class declaration by a comma -

class BaseA
{
};
class BaseB
{
};
Class Derived: public BaseA,public BaseB
{
}

Constructors and Destructors

In C++ more than one constructor will be called when an object of a derived class is created. When any derived class is instantiated the constructor and deconstructor of the base call will also be called.  Base class objects are instantiated before the derived class. Constructors are invoked in order from the top-most (most base-level) class, down to the most derived class. Destructors are invoked in the reverse order. The following example illustrates the order of execution of constructors and destructors in inheritance

#include <iostream>
using namespace std;
class BaseA
{
	public:
		BaseA()
		{
			cout<<"BaseA's Constructor"<<endl;
		}
		~BaseA()
		{
			cout<<"BaseA's Destructor"<<endl;
		}
};
class BaseB : BaseA
{
	public:
		BaseB()
		{
			cout<<"BaseB's Constructor"<<endl;
		}
		~BaseB()
		{
			cout<<"BaseB's Destructor"<<endl;
		}
};
class BaseC : BaseB
{
	public:
		BaseC()
		{
			cout<<"BaseC's Constructor"<<endl;
		}
		~BaseC()
		{
			cout<<"BaseC's Destructor"<<endl;
		}
};
int main()
{
	BaseC c;
	return 0;
}

Passing Parameters during Base Class Initialisation

If a base class contains an overloaded constructor that requires arguments at the time of instantiation then the base class can be initialised by invoking the appropriate base class constructor via the constructor of the derived class-

class Base
{
public:
Base(int value) // overloaded constructor
{
}
};
Class Derived: public Base
{
public:
Derived(): Base(5) // instantiate Base with argument 5
{
// derived class constructor code
}
}

Base Class Pointer to Derived Class Object

C++ Inheritance allows base class pointers to point to derived class objects as both the derived class and base class are pointer type-compatible. 

base *pBase; //declare pointer to type base
derived obj; //instantiate derived on obj
pBase = &obj;//set pointer to address of obj

In the the code section below a base class pointer pBase points to the derived class object obj. Since pointer pBase cannot access derived class members, then derived class members are not available to the base class pointer. 

#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base Constructer" << endl;
}
    
void Test()
{
cout << "base class function " << endl;         
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived Constructor" << endl;
}
void  Test()
{
cout << "derived class function " << endl;          
}
};

int main() {
Base* pBase;
Derived obj;
pBase=&obj;
pBase->Test();
return 0;
}

Virtual functions

A virtual function is a member function declared within a base class and re-defined and overridden by a derived class. When referring to a derived class object using a pointer or a reference to the base class, the derived class function can be called by declaring the function 'virtual'. When a function is made virtual, C++ determines which function is to be invoked at the runtime based on the type of the object pointed to by the base class pointer and hence is known as dynamic linkage or late binding.  

The code sample below demonstrates both early and late binding -

#include<iostream>
using namespace std;
class Base
{
 public:
void virtual virtfunc()
 {
  cout << "Base Virtual Class" << endl;
 }
   void  func()
 {
  cout << "Base non virtual class" << endl;
 }
};
class Derived:public Base
{
 public:
 void virtfunc()
 {
  cout << "Derived Class" << endl;
 }
  void func()
 {
  cout << "Derived Class non virtual" <<endl;
 }
};
 int main()
{
Base *b;     //Instantiate base class pointer
Derived d;   //instantiate derived class 
b=&d;        //assign derived class object to a base class object
b->func();     // on-virtual function, binded at compile time
b->virtfunc(); // Virtual function, binded at runtime
}

Virtual Destructors

A virtual destructor ensures that when a derived subclass goes out of scope or is deleted the order of destruction of each class is carried out in the correct order. When a pointer to a base class is assigned to a derived class object and that object is deleted, then the base class destructor will be called instead of the derived class destructor. To address this situation, the base class should be defined with a virtual destructor to ensure that the object of the derived class is destructed properly. 

In the worked example below a derived class is instantiated both on the free store and the stack. When the stack object goes out of scope the base destructor is automatically called after the derived deconstructor. To ensure the correct destructor sequence for the base class instantiated using the derived class object it is necessary to include a virtual destructor.  Failure to do so would mean only the base class destructor class being called resulting in memory leak.    

#include <iostream>
using namespace std;
class Base {
public:
    Base() {
        cout << "Base Constructer" << endl;
    }
    virtual ~Base() // virtual destructor
    {
        cout << "Base Destructor" << endl;
    }
};
class Derived : public Base {
public:
    Derived() {
        cout << "Derived Constructor" << endl;
    }
    ~Derived() {
        cout << "Derived Destructor" << endl;
    }
};
void DeleteMemory(Base* pBase) {
    delete pBase;
}
int main() {
    cout << "Allocating a derived object on the free store:" << endl;
    Base* pBase = new Derived;
    cout << "Deleting derived class " << endl;
    DeleteMemory(pBase);
    cout << "Instantiating a derived class on the stack:" << endl;
    Derived drv;
    cout << "Automatic destruction as it goes out of scope: " << endl;
    return 0;
}

Override

The override identifier makes the compiler check the base class(es) to see if there is an equivalent virtual function to that in the derived class. If there is not, the compiler will indicate an error.

class Base
{
virtual void method(int);
};
class Derived : public Base
{
virtual void method(float) override; // This will produce an error
};

Avoiding Inheritance Using final

In C++11 the ability to prevent inheriting from a class or to prevent the overriding of a single method is done with the special identifier final. Any attempts to derive a class from a base class marked as final will result in a compiler error - 

class Base final// no class can be derived from class Base
{
method();
};
class Base {
virtual void method() final;// no class can override method()
};