Skip to content

Instantly share code, notes, and snippets.

@twentyse7en
Last active November 29, 2020 16:27
Show Gist options
  • Save twentyse7en/c2a4447d2fafc8548a2b330e4ce9cc39 to your computer and use it in GitHub Desktop.
Save twentyse7en/c2a4447d2fafc8548a2b330e4ce9cc39 to your computer and use it in GitHub Desktop.
Inheritance in depth

Inheritance

Advantages

  • The main advantages of inheritance are code reusability and readability. When child class inherits the properties and functionality of parent class, we need not to write the same code again in child class. This makes it easier to reuse the code, makes us write the less code and the code becomes much more readable.

Types

  1. Single inheritance
  2. Multilevel inheritance
  3. Multiple inheritance
  4. Hierarchical inheritance
  5. Hybrid inheritance

Single inheritance

In Single inheritance one class inherits one class exactly.(example is trivial)

multilevel inheritance

In this type of inheritance one class inherits another child class. ex:
C inherits B and B inherits A

Multiple Inheritance

In multiple inheritance, a class can inherit more than one class. This means that in this type of inheritance a single child class can have multiple parent classes.

#include<iostream>

class A {
    public:
        A()
        {
            std::cout << "Constructor of class A" << std::endl;
        }
};

class B {
    public:
        B(){
            std::cout << "Constructor of class B" << std::endl;
        }
};

class C : public A, public B
{
    public:
        C(){
            std::cout << "Constructor of class C" << std::endl;
        }
};

int main()
{
    C c1;
    return 0;
}

Output

Hierarchical Inheritance

In this type of inheritance, one parent class has more than one child class.

Hybrid Inheritance

Hybrid inheritance is a combination of more than one type of inheritance. For example, A child and parent class relationship that follows multiple and hierarchical inheritance both can be called hybrid inheritance.

Dreaded Diamond problem

Problem with multiple inheritance

#include<iostream>
using namespace std;

class USBDevice
{
    private:
        long m_Id;

    public:
        USBDevice(long id)
            : m_Id(id)
        {
        }

        long getId() { return m_Id; }
};


class NetworkDevice 
{
    private:
        long m_Id;

    public:
        NetworkDevice(long Id)
            : m_Id(Id)
        {
        }

        long getId() { return m_Id; }

};

class WirelessAdapter: public USBDevice, public NetworkDevice 
{
    private:
        long m_Id;

    public:
        WirelessAdapter(long usbId, long networkId)
            : USBDevice(usbId), NetworkDevice(networkId)
        {
        }

};

int main()
{
    WirelessAdapter c546(345, 564);
    
    std::cout << c546.getId() << std::endl;
}

Output

When c54G.getID() is compiled, the compiler looks to see if WirelessAdapter contains a function named getID(). It doesn’t. The compiler then looks to see if any of the parent classes have a function named getID(). See the problem here? The problem is that c54G actually contains TWO getID() functions: one inherited from USBDevice, and one inherited from NetworkDevice. Consequently, this function call is ambiguous, and you will receive a compiler error if you try to compile it.

However, there is a way to work around this problem: you can explicitly specify which version you meant to call

int main()
{
    WirelessAdapter c54G(5442, 181742);
    std::cout << c54G.USBDevice::getID();
 
    return 0;
}

#include<iostream>

class PoweredDevices
{
    public:
        PoweredDevices()
        {
            std::cout << "Powered Devices" << std::endl;
        }
};

class Printer: public PoweredDevices
{
    public:
        Printer()
        {
            std::cout << "Printer" << std::endl;
        }
};

class Scanner: public PoweredDevices
{
    public:
        Scanner()
        {
            std::cout << "Scanner" << std::endl;
        }
};

class Copier: public Printer, public Scanner 
{
    public:
        Copier()
        {
            std::cout << "Copier" << std::endl;
        }
};


int main()
{
    Copier c1;
}

Output

If you were to create a Copier class object, by default you would end up with two copies of the PoweredDevice class -- one from Printer, and one from Scanner.
While this is often desired, other times you may want only one copy of PoweredDevice to be shared by both Scanner and Printer.

There are many issues that arise in this context, including whether Copier should have one or two copies of PoweredDevice, and how to resolve certain types of ambiguous references. While most of these issues can be addressed through explicit scoping, the maintenance overhead added to your classes in order to deal with the added complexity can cause development time to skyrocket. We’ll talk more about ways to resolve the diamond problem in the next lesson.

Virtual Base class

To share a base class, simply insert the “virtual” keyword in the inheritance list of the derived class. This creates what is called a virtual base class, which means there is only one base object. The base object is shared between all objects in the inheritance tree and it is only constructed once.

However, this leads to one more problem: if Scanner and Printer share a PoweredDevice base class, who is responsible for creating it? The answer, as it turns out, is Copier. The Copier constructor is responsible for creating PoweredDevice. Consequently, this is one time when Copier is allowed to call a non-immediate-parent constructor directly:

#include<iostream>

class PoweredDevices
{
    public:
        PoweredDevices()
        {
            std::cout << "Powered Devices" << std::endl;
        }
};

class Printer: virtual public PoweredDevices
{
    public:
        Printer()
        {
            std::cout << "Printer" << std::endl;
        }
};

class Scanner: virtual public PoweredDevices
{
    public:
        Scanner()
        {
            std::cout << "Scanner" << std::endl;
        }
};

class Copier: public Printer, public Scanner 
{
    public:
        Copier()
        {
            std::cout << "Copier" << std::endl;
        }
};


int main()
{
    Copier c1;
}

Ouput

Few details

  • virtual base classes are always created before non-virtual base classes, which ensures all bases get created before their derived classes.
  • note that the Scanner and Printer constructors still have calls to the PoweredDevice constructor. When creating an instance of Copier, these constructor calls are simply ignored because Copier is responsible for creating the PoweredDevice, not Scanner or Printer. However, if we were to create an instance of Scanner or Printer, those constructor calls would be used, and normal inheritance rules apply.
  • if a class inherits one or more classes that have virtual parents, the most derived class is responsible for constructing the virtual base class. In this case, Copier inherits Printer and Scanner, both of which have a PoweredDevice virtual base class. Copier, the most derived class , is responsible for creation of PoweredDevice. Note that this is true even in a single inheritance case: if Copier was singly inherited from Printer, and Printer was virtually inherited from PoweredDevice, Copier is still responsible for creating PoweredDevice.
  • All classes inheriting a virtual base class will have a virtual table, even if they would normally not have one otherwise, and thus be larger by a pointer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment