FURTHER READINGS :-
Unit 4: Chapter 7 Inheritance
Single inheritance
Multiple inheritance
Multilevel inheritance
Hybrid inheritance
Hierarchical inheritance
Defining a derived class
Defining derived class constructors
7.1 Introduction
Reusability is yet another important feature of OOP. It is always nice if we could, reuse something that already exists rather than trying to create the same all over again. It would not only save time and money but also reduce frustration and increase reliability. For instance, the reuse of a class that has already been tested, debugged and used many times can save us the effort of developing and testing the same again.
Fortunately, C++ strongly supports the concept of reusability. The C++ classes can be reused in several ways. Once a class has been written and tested, it can be adapted by other programmers to suit their requirements. This is basically done by creating new classes, reusing the properties of the existing ones. The mechanism of deriving a new class from an old one is called inheritance (or derivation). The old class is referred to as the base class and the new one is called the derived class or subclass.
The derived class inherits some or all of the traits from the base class. A class can ^ inherit properties from more than one class or from more than one level. A derived el&| with only one base class, is called single inheritance and one with several base classes 4 called multiple inheritance. On the other hand, the traits of one class may be inherited^ more than one class. This process is known as
hierarchical inheritance. The mechanism $ deriving a class from another ‘derived class’ is known as multilevel inheritance. Figure 8.1 shows various forms of inheritance that could be used for writing
extensible programs. Tfo direction of arrow indicates the direction of inheritance. (Some authors show the arrow® opposite direction meaning <<inherited from<<.)
The colon indicates that the derived-class-name is derived from the base-class-name. The visibility-mode is optional and, if present, may be either private or public. The default r visibility-visibility-mode is private. Visibility mode specifies whether the features of the base class are privately derived or publicly derived;
Examples:
class ABC: private XYZ // private derivation {
members of ABC };
class ABC: public XYZ // public derivation {
members of A8C };
class ABC: XYZ // private derivation by default {
members of ABC };
When a base class is privately inherited by a derived class ‘public members’ of the base class become
‘private members’ of the derived class and therefore the public members of the base class can only be accessed by the member-functions of the derived class. They are inaccessible to the objects of the derived class. Remember, a public member of a class can be accessed by its own objects using the dot operator. The result is that no member of the base class is accessible to the objects of the derived class.
On the other hand, when the base class is publicly inherited, ‘public members’ of the base class become ‘public members’ of the derived class and therefore they are accessible to the objects of the derived class. In both the cases, the private members are not inherited and therefore, the private members of a base class will never become the members of its derived class.
In inheritance, some of the base class data elements and member functions are 'inherited1 into the derived class. We can add our own data and member functions and thus extend the functionality of the base class. Inheritance, when used to modify and extend the capabilities of the existing classes, becomes a very powerful tool for incremental program development.
7.2 Single Inheritance:
Let us consider a simple example to illustrate inheritance. Program 8.1 show's a base class B and a derived class D. The class B contains one private data member, one public data member, and three public member functions. The class D contains one private data member and two public member functions. void B :: get_ab(void)
{
a * 5; b = 10;
}
int B :: get_a()
cout << <<a = << << a << <<\n<<;
}
cout << <<a = << << qet_a() << <<\n<<;
cout << <<b = << << b << <<\n<<;
cout << <<c = << << c << <<\n\n<<;
} Given below is the output of Program 8.1:
a = 5
The class D is a public derivation of the base class B. Therefore. D inherits all the public members of B and retains their visibility. Thus a public member of the base class B is also a public member of the derived class D. The private members of B cannot be inherited by D. The class D, in effect, will have more members than what it contains at the time of declaration as shown in Fig. 8.2.
Fig. 8.2 Adding more members to a class (by public derivation)
The program illustrates that the objects of class D have access to all the public members of B. Let us have a look at the functions show_a() and mul():
void show a() {
cout << “a = “ << a << “\n” <<;
}
void mul () {
c = b * get_a(); // c = b * a }
Although the data member a is private in B and cannot be inherited, objects of D are able to access it through an inherited member function of B.
Let us now consider the case of private derivation.
class B {
int a;
public:
Class D
int b;
void get_ab();
void get_a();
void show a();
};
class D : private B // private derivation {
int c;
public:
void mul();
void display();
};
The membership of the derived class D is shown in Fig. 8.3. In private derivation, the public members of the base class become private members of the derived class. Therefore, the objects of D can not have direct access to the public member functions of B.
Fig. 8.3 = Adding more members to a class (by private derivation) The statements such as
d.get_ab(); // get_ab() is private d.get_a(); // so also get_a() d.show_a(); // and show_a()
will not work. However, these functions can be used inside mul() and display() like the normal functions as shown below:
void mul() {
get_ab();
c = b * get a();
Class D
J
Program 8.2 incorporates these modifications for private derivation. Please compare this with Program 8.1.
SINGLE INHERITANCE : PRIVATE
#include <iostream>
using namespace std;
class B {
int a; // private; not inheritable public:
int b; // public; ready for inheritance void get_ab();
int get_a(void);
void show_a(void);
};
class D : private B // private derivation { void B :: get_ab(void)
{
cout << “Enter values for a and b”;
cin >> a >> b;
{ The output of Program 8.2 would be:
Enter values for a and b:5 10
Suppose a base class and a derived class define a function of the same name. What will happen when a derived class object invokes the function?. In such cases, the derived class function
supersedes the base class definition. The base class function, will be called only if the derived class does not redefine the function.
Making a Private Member Inheritable
We have just seen how to increase the capabilities of an existing class without modifying it. We have also seen that a private member of a base class cannot be inherited and therefore it is not available for the derived class directly. What do we do if the private data needs to be inherited by a derived class? This can be accomplished by modifying the visibility limit of the private member by making it
public. This would make it accessible to all the other functions of the program, thus taking away the advantage of data hiding.
C++ provides a third visibility modifier, protected, which serve a limited purpose in inheritance. A member declared as protected is accessible by the member functions within its class and any class immediately derived from it. It cannot be accessed by the functions outside these two classes. A class can now use all the three visibility modes as illustrated below:
class alpha {
private: // optional
... // visible to member functions ... // within its class
protected:
... // visible to member functions ... // of its own and derived clasees Public:
... // visible to all functions ... // in the progrom };
When a protected member is inherited in public mode, it becomes protected in the derived class too and therefore is accessible by the member functions of the derived class. It is also ready for further inheritance. A protected member, inherited in the private mode derivation, becomes private in the derived class. Although it is available to the member functions of the derived class, it is not available for further inheritance (since private members cannot be inherited). Figure 8.4 is the pictorial representation for the two levels of derivation.
Class B
Fig 8.4 Effect of inheritance on the visibility of members
The keywords private, protected, and public may appear in any order and any number of times in the declaration of a class. For example.
class beta {
protected:
...
public:
...
private:
...
public:
...
};
is a valid class definition.
However, the normal practice is to use them as follows:
class beta {
... // private by default ...
protected:
...
...
public:
...
}
It is also possible to inherit a base class in protected mode < known as protected derivation). In protected derivation, both the public and protected members of the base class become protected members of the derived class. Table 8.1 summarizes how the visibility of base class members undergoes modifications in all the three types of derivation.
Now let us review the access control to the private and protected members of a class. What are the various functions that can have access to these members? They could be:
1. A function that is a friend of the class.
2. A member function of a class that is a friend of the class.
3. A member function of a derived class.
While the friend functions and the member functions of a friend class can have direct access to both the private and protected data, the member functions of a derived class can directly access only the protected data. However, they can access the private data through the member functions of the base class. Figure 8.5 illustrates how the access control mechanism works in various situations. A simplified view of access control to the members of a class is shown in Fig. 8.6.
Derived class visibility
Base class visibility Public
derivation
Private derivation
Protected derivation Private ---> Not inherited Not inherited Not inherited
Protected ---> Protected Private Protected
Public ---> Public Private Protected
class X
Fig. 8.5 <=> Access mechanism in classes 7.3 Multilevel Inheritance
It is not uncommon that a class is derived from another derived class as shown in Fig. 8.7. The class A serves as a base class for the derived class B, which in turn serves as a base class for the derived class C. The class B is known as intermediate base class since it provides a link for the inheritance between A and C. The chain ABC is known as inheritance path.
A derived class with multilevel inheritance is declared as follows:
class A{... }; // Base class class B: public A {... }; // B derived from A class C: public B {... }; // C derived from B This process can be extended to any number of levels.
Let us consider a simple example. Assume that the test results of a batch of students are stored in three different classes. Class student stores the roll-number, class test stores the marks obtained in two subjects and class result contains the total marks obtained in the test. The class result can inherit the details of the marks obtained in the test and the roll- number of students through multilevel inheritance. Example:
class student {
protected:
int roll_number;
public:
void getnumber(int);
void put_number(void);
};
void student :: get number(int a) {
roll number = a;
}
void student :: put_number() {
cout « "Roll Number: " « roll number « "\n";
}
class test : public student // First level derivation
{
protected:
float sub1;
float sub2;
public:
void getmarks(float, float);
void put marks(void);
};
void test :: get marks(float x, float y) {
class result : public test // Second level derivation
{
float total; // private by default public:
void display(void);
};
The class result, after inheritance from ‘grandfather' through 'father', would contain the following members:
private:
float total; // own member
protected:
Int roll_number; // inherited from student via test
float sub1; // inherited from test
float sub2; // inherited from test
public:
void get_number(int); // from student via test void put_number(void); // from student via test void get _marks(float, float); // from test
void put_marks(void); // from test
void display(void); // own member
The inherited functions put_number() and put _marks() can be used in the definition of display () function:
void result :: display(void) {
Here is a simple main() program:
int ma1n() {
result student1; // student 1 created student1.get_number(111);
studentl.get_marks(75.0, 59.5);
studentl.display();
return 0;
}
This will display the result of student1. The complete program is shown in Program 8.3.
MULTILEVEL INHERITANCE
#include <iostream>
using namespace std;
class student
void student :: get number(int a) {
roll_number = a;
}
void student :: put number() {
cout « "Roll Number: " « roll number « "\n”;
}
class test : public student // First level derivation (
protected:
float sub1;
float sub2;
public:
void get_marks(f1oat, float);
void put_marks(void);
};
void test :: getmarks(float x, float y) {
sub1 = x;
sub2 = y;
}
void test :: put_marks()
{
cout « "Marks in SUB1 = “ « subl « “\n";
cout « "Marks in SUB2 = “« sub2 « "\n";
}
class result : public test // Second level derivation {
float total; // private by default public:
void display(void);
};
void result :: display(void) {
total = subl + $ub2;
put_number();
put_marks();
cout « "Total = * « total « "\n";
}
int main() (
result student1; // student 1 created student1.get nuntber(111);
student1.get_marks(75.0, 59.5);
student1.display();
return 0;
}
Program 8.3
Program 8.3 displays the following output:
Roll Number: 111 Marks In SUB1 = 75 Marks in SUB2 =59.5 Total = 134.5
7.4 Multiple Inheritance
A class can inherit the attributes of two or more classes as shown in Fig. 8.8. This is known as multiple inheritance. Multiple inheritance allows us to combine the features of several existing classes as a starting point for defining new classes. It is like a child inheriting the physical features of one parent and the intelligence of another.
The syntax of a derived class with multiple base classes is as follows:
where, visibility may be either public or private. The base classes are separated by commas.
Example:
class P : public M, public N {
public:
void display(void);
}
Classes M and N have been specified as follows:
class M {
protected:
int m;
public:
void get_m(int);
};
void M :: get_m(int x) {
m = x;
) class N {
protected:
int n;
class D: visibility B-1, visibility B-2 ...
{
...
... (Body of D) ...
}
public:
The derived class P, as declared above, would, in effect, contain all the members of M and N in addition to its own members as shown below:
class P
void display(void); // own member
};
The member function displayf) can be defined as follows:
void P :: display(void) {
cout « "m " « m « "\n“;
cout « “n “ « n « "\n";
cout « "m*n =" « m*n « "\n";
};
The main() function which provides the user-interface may be written as follows:
main()
Program 8.4 shows the entire code illustrating how all the three classes are implemented in multiple inheritance mode.
int m;
void P :: dlsplay(void) { The output of Program 8.4 would be:
m = 10 n = 20 m*n = 200
7.5 Hierarchical Inheritance
We have discussed so far how inheritance can be used to modify a class when it did not satisfy the requirements of a particular problem on hand. Additional members are added through inheritance to extend the capabilities of a class. Another interesting application of inheritance is to use it as a support to the hierarchical design of a program. Many programming problems can be cast into a hierarchy where certain features of one level are shared by many others below that level.
As an example, Fig. 8.9 shows a hierarchical classification of students in a university. Another example could be the classification of accounts in a commercial bank as shown in Fig. 8.10. All the students have certain things in common and, similarly, all the accounts possess certain common features.
Fig. 8.9 o Hierarchical classification of students
In C++, such problems can be easily converted into class hierarchies. The base class will include all the features that are common to the subclasses. A subclass can be constructed by inheriting the properties of the base class. A subclass can serve as a base class for the lower level classes and so on.
7.6 Hybrid Inheritance
There could be situations where we need to apply two or more types of inheritance to design a program. For instance, consider the case of processing the student results discussed in Sec. 8.5.
Assume that we have to give weightage for sports before finalising the results. The weightage for sports is stored in a separate class called sports. The new inheritance relationship between the various classes would be as shown in Fig. 8.11.
The sports class might look like:
class sports {
protected:
float score;
public:
void get_score(float);
void put_score(void);
}
The result will have both the multilevel and multiple inheritances and its declaration would be as follows:
class result : public test, public sports {
...
...
};
Where test itself is a derived class from student. That is class test : public student
{
...
...
}
Program 8.5 illustrates the implementation of both multilevel and multiple inheritance.
#include <iostream>
using namespace std;
class student {
protected:
class test : public student {
protected:
float part1, part2;
public:
void get_marks (float x, float y) {
class result : public test, public sports {
float total;
public:
void display(void);
};
void result :: display(void) { Here is the output of Program 8.5:
Roll No: 1234
7.7 Constructors in Derived Classes
As we know, the constructors play an important role in initializing objects. We did not use them earlier in the derived classes for the sake of simplicity. One important thing to note here is that, as long as no base class constructor takes any arguments, the derived class need not have a constructor function. However, if any base class contains a constructor with one or more arguments, then it is mandatory for the derived class to have a constructor and pass the arguments to the base class constructors. Remember, while applying inheritance we usually create objects using the derived class. Thus, it makes sense for the derived class to pass arguments to the base class constructor.
When both the derived and base classes contain constructors, the base constructor is executed first and then the constructor in the derived class is executed.
In case of multiple inheritance, the base classes are constructed in the order in which they appear in the declaration of the derived class. Similarly, in a multilevel inheritance, the constructors will be executed in the order of inheritance.
Since the derived class takes the responsibility of supplying initial values to its base classes, we supply the initial values that are required by all the classes together, when a derived class object is declared. How are they passed to the base class constructors so that they can do their job? C++
supports a special argument passing mechanism for such situations.
The constructor of the derived class receives the entire list of values as its arguments and passes them on to the base constructors in the order in which they are declared in the derived class. The base constructors are called and executed before executing the statements in the body of the derived constructor.
The general form of defining a derived constructor is:
The header line of derived-constructor function contains two parts separated by a colon(:).The header line of derived-constructor function contains two parts separated by a colon!:). The first part provides the declaration of the arguments that are passed to the derived- constructor and the second part lists the function calls to the base constructors.
base1(arglist1), base2(arglist2) ... are function calls to base constructors base1(), base2(), ... and therefore arglist1, arglist2, ... etc. represent the actual parameters that are passed to the base constructors. Arglist1 through ArglistN are the argument declarations for base constructors base
base1(arglist1), base2(arglist2) ... are function calls to base constructors base1(), base2(), ... and therefore arglist1, arglist2, ... etc. represent the actual parameters that are passed to the base constructors. Arglist1 through ArglistN are the argument declarations for base constructors base