Inheritance and Composition

I begin this article by explaining the necessity of inheritance, and how a development team uses inheritance to write maintainable code. Then I proceed to explain why do we have the three access specifiers. My approach in building these concepts is not the usual textbook one. I have written it from a practical viewpoint, but without missing on the usual meanings of these concepts. The article ends with an explanation of composition, and how inheritance and composition differ.

Last Reviewed and Updated on February 7, 2020
Posted by Parveen(Hoven),
Aptitude Trainer and Software Developer

My C/C++ Videos on Youtube

Here is the complete playlist for video lectures and tutorials for the absolute beginners. The language has been kept simple so that anybody can easily understand them. I have avoided complex jargon in these videos.

Inheritance can be thought of as an evolution. I have a C++ class today. It is serving my purpose well; it meets the needs of a business. A class is there, after all, to meet some real world objectives. With time new requirements come, so the existing functions need a modification, or new functions need to be added. Inheritance in C++ allows us to extend existing classes into new classes by adding more functions or by redefining the older ones. It allows us to create new classes - the evolved ones. The new classes are different entities but more powerful and current, just like the newer generations of human beings.

What is the Purpose of Inheritance? Why Inheritance?

This is the most widely asked question. Why do we need to create a new class, and not add functions to the existing class, or erase the existing functions and write newer ones.

To understand all this put yourself in the position of a vendor company that has written a class and used it in a product that has been sold to the buyers. If that class has been debugged and tested, and is a finally settled piece of code, then we can say that we have a sort of a "restore point" or a bookmark here. We can be sure that things have worked till that point. That class has been customized to different buyers by probably changing the values of certain constants, like the company name or logo, but the class code is otherwise the same. The vendor has one single class that suits the needs of all customers. If the customer makes changes to this class to meet the needs of a new customer then he has two un-related classes - the second class has 90% of the functions that have been copied and pasted from the first one, and 10% of the stuff is the addition. This type of arrangement is fraught with problems of maintenance because you have two carbon copies of the same code. If some minor changes ever need to be made in the code then same changes will have to be made at numerous places - in all classes wherever that code has been copied and replicated. Always remember the basic principle - Do not Repeat Yourself - the DRY principle.

So, adding functions to an existing class is not a viable option. It might not be an option if you do not have the source code with you, like when you have purchased compiled code from a third party.

The next possibility is to not to disturb a class that is already settled and fine. A more pragmatic approach is to write a new class that contains just the new additions and modifications. In this way you have two classes but there is no repetition of code. Older customers, and those customers who do not want to use the extended features can be served through the older base class, and those who need updated, extended features can be served the code compiled with the derived class.

How could two independent classes be connected so that data and functions of the base class become available in the derived class, without requiring a copy paste? This is the main issue. For this C++ provides the concept of inheritance. A derived class can acquire data members and functions of its base class if it is written with the inheritance syntax.

How a class Inherits from Another

The syntax as shown below is used to establish a parent child relationship between two classes.

class CBase
{

public:
    void fx()
    {

        cout << "CBase::fx called!\n";

    }

};

class CDerived : public CBase
{

public:
    void gx()
    {

        cout << "CDerived::fx called!\n";

    }

};

int main ()
{

    CDerived cd;

    cd.fx();

    cd.gx();

    return 0;

}


The class CDerived is an extension of CBase because of ": public CBase" in the class definition. I will discuss this "public" later. Notice that by writing CDerived we have added a function gx to the class CBase. Both the functions gx and fx are available on an object of the CDerived class.

Inheritance and Access Specifiers

All the public members of a base class are available to the derived class. They can be accessed from outside by creating an object of the class. The private members of a base class are not accessible to the derived class. There is a third access specifier - the protected keyword. protected members of a base class are accessible to the derived class, but not to the outside world.

We have added a private and protected function to the base class CBase. The protected function protfx can be accessed in the derived class but not from main. The private function pvtfx of the base class can't be accessed both in the derived class and main.

class CBase
{

    void pvtfx()
    {

        cout << "CBase::pvtfx called!\n";

    }

protected:
    void protfx()
    {

        cout << "CBase::protfx called!\n";

    }

public:
    void fx()
    {

        cout << "CBase::fx called!\n";

    }

};

class CDerived : public CBase
{

public:
    void gx()
    {

        // protected can be accessed
        protfx();

        cout << "CDerived::fx called!\n";

        // COMPILER ERROR
        // pvtfx();

    }

};

int main ()
{

    CDerived cd;

    cd.fx();

    cd.gx();

    // ERROR private
    // and protected can't be
    // accessed outside
    // cd.pvtfx();

    // cd.protfx();

    return 0;

}


There is more to the C++ language than it appears on the surface. Suppose a developer is writing a class, and his design requires that a certain member of the class be not be altered by derived classes. As far as the lone developer is concerned, he himself knows it. But a code is maintained for years. During this course other developers come and go. How should the original developer's intention be propagated to others, his successors? Or even to himself, if he comes back to work on his own code after 5 years? One way is to maintain a log book that contains a date wise diary, and a list of important things, sort of an office file. This looks fine, but it itself can run into severe maintenance problems. And, some lazy ones might not read the whole log book carefully. There is a more robust way that C++ provides: the built-in protection - through the access specifier. It provides for all the three possibilities! As I have said in one of my posts earlier, C++ is not about writing code, C language is much better for that. C++ is about writing maintainable code, with each keyword carrying a significance, with each keyword as a messenger for conveying the original developer's intent to his successors.

What is public, private and protected Inheritance?

The most common is the public inheritance. The other two are rarely used. They are in the language probably for completeness. The type of inheritance affects the visibility of the members of the base and derived class. Following is a nice summary.

class A
{

public:
    int x;

protected:
    int y;

private:
    int z;

};

class B : public A
{

    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{

    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : A // 'private' is default
{

    // x is private
    // y is private
    // z is not accessible from D
};


Is-A Relationship

A derived class is an extension of a base class. It is a child of the base class. For example, if we have a class called CVehicle, and we create a new class CBike out of it, then we can say that CBike is a type of CVehicle. It should be fine because CBike contains everything that a CVehicle contains - both have wheels, both have a speed, both can accelerate, and so on. When a class is derived from a base class, an is-a relationship is established between the two.

Consider a class CVehicle, and its extension CBike as shown below.

class CVehicle
{

public:
    void Start()
    {

        cout << "Started" << endl;

    }

};

class CBike : public CVehicle
{

public:
    void Wheels()
    {

        cout << "2 wheels" << endl;

    }

};

int main ()
{

    CVehicle& cv = CBike();

    cv.Start();

    return 0;

}


If you noticed above, the CVehicle reference points to an object of CBike - something that you haven't seen earlier. This statement is possible because CBike is a type of CVehicle, so we can reference a bike as a vehicle, just as we do it in day to day parlance.

You can also use a base class pointer to access a derived class object. This is shown below.

int main ()
{

    CVehicle* cv = new CBike();

    cv->Start();

    // cast the vehicle to CBike
    // so that correct destructor can be called
    // C++ has a better way for this, but
    // let's wait for now, and do this
    delete static_cast<CBike*>(cv);

    return 0;

}


The release of memory requires a cast back to the original type so that the destructor of the derived class can be called. Otherwise the destructor for the base class only will be called; which could skip some cleanup through the derived class destructor.

Base Class with a non-Default Constructor

If the base class doesn't have a default constructor then the derived class must make a call to the base class constructor so that the inherited portion of the code can be properly initialized. The call to the base class constructor is made in the member initializer list. The reason is that the base class component should be initialized before the constructor code for the derived class is called. This is an example code.

class CBase
{

public:
    CBase(int x)
    {

        cout << "CBase::ctor called!\n";

    }

    ~CBase()
    {

        cout << "CBase::dtor called!\n";

    }

};

class CDerived : public CBase
{

public:
    CDerived (int i) : CBase(i)
    {

        cout << "CDerived::ctor called!\n";

    }

    ~CDerived()
    {

        cout << "CDerived::dtor called!\n";

    }

};

int main ()
{

    CDerived cd(9);

    return 0;

}

// output
CBase::ctor called!
CDerived::ctor called!
CDerived::dtor called!
CBase::dtor called!

The output of the code shows the order of constructor and destructor calls also. The parent class constructor is the first to be called. Since the derived class is an extension of the parent class, it is expected that the base class portion should be completely initialized before the next things happen. On the same lines, we can see that the base class destruction must occur after the destruction of the derived class.

Redefinition of Base class Functions

I told you one of the purposes of inheritance is to improve upon the base class functionality. This means a "rewriting" of a base class function, and replacing it, or hiding it. Redefinition is done by writing a new function of the same signature, like shown below. You can run it to see that when the function fx is called on CDerived, the base class version is replaced by the redefined one.

class CBase
{

public:
    void fx()
    {

        cout << "CBase::fx called!\n";

    }

};

class CDerived : public CBase
{

public:
    // redefinition or replacement
    void fx()
    {

        cout << "CDerived::fx called!\n";

    }

};

int main ()
{

    CDerived cd;

    cd.fx();

    return 0;

}

// output
CDerived::fx called!

Calling the Base class Version

A redefinition causes the base class version to be hidden. But what if we want the code of the base class to be utilized in the derived class. An example is where a base class function has to be rewritten by appending one or two lines to the entire base class function. There is no point in copy and pasting the base class lines into the new function. The scope resolution operator is used to call the base class version of a function as shown below. The scope resolution operator can be used for accessing the data members also.

class CBase
{

protected:
    int i;

public:
    CBase (int x) : i(x)
    {

    }

public:
    void fx()
    {

        cout << "CBase::fx called!\n";

    }

};

class CDerived : public CBase
{

public:
    CDerived(int x) : CBase(x)
    {

    }

public:
    // redefinition or replacement
    void fx()
    {

        // insert a call to the
        // base class function fx
        CBase::fx();

        cout << "CDerived::fx called!\n";

        // access the base class i
        cout << CBase::i << endl;

    }

};

int main ()
{

    CDerived cd(9);

    cd.fx();

    return 0;

}

// output
CBase::fx called!
CDerived::fx called!
9

Constructors are not Inherited. Why?

A derived class doesn't inherit constructors from its base class. Why is it so? Basically, a constructor is used to initialize data members of a class. Since a derived class has its own data members, it has to have its own constructor too. A base class cannot have any information about its derivatives. The constructor of a derived class needs to perform additional actions that a base-class constructor doesn't have to do and has no information about. These additional actions are the initialization of the data members of the derived class.

Embedded Objects of Other Classes(Composition)

Composition is creating a new class by hosting objects of other classes. This is not same as inheritance. Inheritance is about extending an existing class by making additions or alterations to it. But composition is not about extension. It is about using the facilities of an existing class, something like outsourcing. A new class is surely being created, but it is being assembled out of existing classes. For example, a car is assembled by adding tyres(tires in US English) obtained from elsewhere. Car is not an extension of a tyre(tire in US English); it is composed of a tyre.

If we have a well tested class, and we know that its public functions can be used to meet the objectives of another class, then we can compose a new class by hosting an object or objects of that class. Composition is has-a relationship, or contains relationship.

In the following example we have a class CCalc that has the ability to add and multiply two numbers. We have created a new class which prints the double and square of a number. To achieve this, it outsources the addition and multiplication to the class CCalc. This is how.

class CCalc
{

    int i;

    int j;

public:
    CCalc(int x, int y) : i(x), j(y)
    {

    }

public:
    int Add()
    {

        return i + j;

    }

    int Mul()
    {

        return i * j;

    }

};

class CMyClass
{

    int m;

    CCalc cc;

public:
    CMyClass(int i) : cc(i, i)
    {

    }

public:
    void Double()
    {

        int d = cc.Add();

        cout << "Double = " << d << endl;

    }

    void Square()
    {

        int sq = cc.Mul();

        cout << "Double = " << sq << endl;

    }

};

int main ()
{

    CMyClass cmc(8);

    cmc.Double();

    cmc.Square();

    return 0;

}


A few points can be noticed here. Firstly, the parameterized constructor for the hosted class object is not called at the point of declaration - instead, it is called in the constructor initializer list, just like we would do while inheriting from a class. The reason for doing it at this point remains the same - the hosted objects must be initialized before the constructor of the current class runs.

If you a class hosts objects of two or more classes, then the constructors run in the same order in which they are declared inside the composed class, not in the order in which they appear in the member initializer list.

// one class
class C1
{

public:
    C1(int x)
    {

        cout << "C1 ctor called!\n";

    }

};

// second class
class C2
{

public:
    C2(int x)
    {

        cout << "C2 ctor called!\n";

    }

};

// composed class
class CMyClass
{

    C1 mc1;

    C2 mc2;

public:
    CMyClass() : mc2(8), mc1(9)
    {

    }

};

int main ()
{

    CMyClass cmc;

    return 0;

}

// output
C1 ctor called!
C2 ctor called!


Creative Commons License
This Blog Post/Article "Inheritance and Composition" by Parveen (Hoven) is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Updated on 2020-02-07. Published on: 2015-12-27