Menu

Saturday, 4 July 2015

INHERITANCE

7.1 Introduction:

Inheritance is a process of creating new classes, called derived classes from the existing class or base class. The derived class inherits all the capabilities of the base class but can add members and refinements of its own. The base class is unchanged by this process. Inheritance has important advantages. Most importantly, it permits code reusability. Reusability is yet another important feature of OOP. 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. Creating new classes, reusing the properties of the existing ones, basically does this. 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.

The derived class inherits some or all of the traits from the base class. A class can also inherit properties from more than one class or from more than one level. A derived class with only one base class is called single inheritance and one with several base classes is called multiple inheritance. On the other hand, more than one class may inherit the traits of one class. This process is known as hierarchical inheritance. The mechanism of deriving a class from another 'derived class' is known as multilevel inheritance. Figure shows various forms of inheritance that could be used for writing extensible programs. The direction of arrow indicates the direction of inheritance.

a)   Single inheritance:                               b)  Multilevel inheritance







 









c) Multiple inheritance                                    e) Hierarchical inheritance








 












7.2 Defining Derived Classes:  

A derived class is defined by specifying its relationship with the base class in addition to its own details.
The general form of defining a derived class is:

class derived-class-name : visibility-mode base-class-name
                        {
                                    ………… //
                                    ………… II members of derived class
…………II
};

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 visibility-mode is private. Visibility mode specifies whether the features of the base class are privately derived or publicly derived.

Examples:
class ABC: private XVZ         //private derivation
{
members of ABC
};
class ABC : public XVZ         //public derivation 3
{
members of ABC
};
class ABC : XVZ                    //private derivation by default
{
members of ABC
};

When a derived class privately inherits a base 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 'inherited' 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.3 Single Inheritance:

A derived class with only one base class is called single inheritance. Consider the program below:

# include <jostream.h>
class Counter                                                               II base class
{
protected:                                                        // NOTE: not private
unsigned int count;                             // count
public:
Counter( ) { count = 0; }                    //constructor, no args
Counter(int c) { count = c; }               // constructor, one arg
int get_count( ) { return count; }        // return count
Counter operator ++ ( )                       // increment count
{
count++;                                              // increment count, return
return Counter(count);                        //an unnamed temporary object
}                                                          // initialized to this count
};
class CountDn : public Counter                      // derived class
{
public:
Counter operator --( )                          // decrement count
{
count--;                                                // decrement count, return
return Counter(count);                        // an unnamed temporary object
}                                                          // initialized to this count
};
void main( )
{
CountDn cl;                                                    // c1 of class CountDn
cout << "\ncl=" << c1.get-count();                  // display c1
cl++;
cl++;
cl++;               
cout << "\ncl=" << cl.get-count( );                  // display it
c1--;
c1--;
cout << .'\nc1=" << c1.get-count( );               // display it
}

7.4 Accessing Base Class Members:

An important topic in inheritance is knowing when objects of the derived class can use a member function in the base class. This is called accessibility.

Substituting Base Class Constructors: In the main ( ) part of  the above program:

CountDn c1;
This causes c1 to be created as an object of class countDn and initialized to 0. There is no constructor in the CountDn class specifier, if you don't specify a constructor, the derived class will use an appropriate constructor from the base class. This flexibility on the part of the compiler using one function because another isn't available is feature of inheritance. Generally the substitution is what you want, but sometimes it can be error also.

Substituting Base Class Member Functions: The object c1 of the countDn class also uses the operator++( ) and get-count ( ) functions from the counter class. The first statement is used to increment c1:


c1++;

and the second statement is used to display the count in c1:

cout << "\nt1=" << t1.get_count( );

Again the compiler, not finding these functions in the class of which c1 is a member, uses member functions from the base class. The ++ operator, the constructors, and the get_count( ) function in the counter class, and the - - operator in the countDn class, all work with objects of type countDn.

7.5 Making A Private Member Inheritable (protected):

C++ provides a third visibility modifier, protected, which serves 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 class
public :
……….                       //visible to ail functions
……….                       //in the program
};

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). Table summarizes how the visibility of members undergo modifications when they are inherited.

Table: Visibility of inherited members

Base class Visibility
Derived class visibility
Public derivation
Private derivation

Private
Not inherited
Not inherited

Protected
Protected
Private

Pubic
Pubic
Private


The keywords private, protected, and public may appear in any order and in 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 :
……….          
            }

The following table summarizes the situation in a different way.

Access
Specifier
Accessible from
Own class
Accessible from
Derived class
Accessible from
Objects outside class
public
yes
yes
Yes
Protected
yes
yes
No
Private
yes
No
No


7.6 Derived Class Constructors:

The compiler will substitute a no-argument constructor from the base class, when there is no constructor for the derived class, but it draws the line at more complex constructors. To make such a definition work we must write a new set of constructors for the derived class, consider the program below.
 # include <iostream.h>  
class Counter
{
protected:                                                        // NOTE: not private unsigned
int count;                                             // count
 public:
Counter( ) { count = 0; }                    // constructor, no args
Counter(int c) { count = c; }               // constructor, one arg
int get_Count( ) { return count.; }      // return count
Counter operator ++ ( )                       //increment count
{
count++;                                              // increment count, return
return counter(Count);                        // an unnamed temporary object
}                                                          // initialized to this count
};
class countDn : public counter
{
public:
CountDn( ) : Counter( )          // constructor, no args
           { }
CountDn(int c) : Counter(c)   // constructor 1 arg
           { }
CountDn operator --( )            // decrement count
{
    count--;                                // decrement count, return
    return countDn(Count);      // an unnamed temporary object
}                                              // initialized to this count
};
void main( )
{
CountDn c1;              
CountDn c2(100);
cout << "\nc1=" << c1.get-Count( );    
cout << " \nc2=" << c2 .get-Count ( ) ;
c1++;
c1++;
c1++;  
cout << "\nc1=" << c1.get-Count( );  
c2--;     c2--;                                                    
cout << "\nc2=" << c2.get-Count( );  
CountDn c3 = c2--;                            
cout << "\ric3=" << c3.get-Count( );
}
This program uses two new constructors in the countDn class. Here is the one argument constructor:

CountDn( ) : Counter( )
{ }
This constructor has an unfamiliar feature: the colon followed by a function name. This construction causes the CountDn ( ) Constructor to call the counter( ) constructor in the base class. In main( ) , when we say

CountDn c1;

the compiler will create an object of type countDn and then call the countDn constructor to initialize it. This constructor will in turn call the counter constructor: which carries out the work. The countDn( ) constructor could add additional statements of its own, but in this case it doesn't need to, So the function body between the braces is empty. The statement:

CountDn c2(100);

in main( ) uses the one-argument constructor in countDn  .This constructor also calls the corresponding constructor in the base class:

CountDn(int c) : Counter(c)         ßargument c is passed to Counter
{ }
This construction causes the argument c to be passed from countDn ( ) to  counter ( ) where it is used to initialize the object. In main( ) , after initializing the c1 and c2 objects, we increment one and decrement the other and then print the results. The one-argument Constructor is also used in an assignment statement:

CountDn c3 = c2--;

7.7 Overriding member functions:


You can use member functions in a derived class that have the same name as those in the base class. You might want to do this so that calls in your program work the same way for objects of both base and derived classes.

// Program to demonstrate overloading functions in base and derived classes
# include <iostream.h>
# include <process.h>                                                 // for exit()
const int MAX = 100;                                                 // maximum size of stack
class Stack
{
protected:                                                                   
int st[MAX];                          
public:
Stack( )                                                            // constructor
        { top = 0; }
void push(int var)                                            // put number on stack
{          st[++top] = var;           }
int pop( )                                                         // take number off stack
{ return st[top--]; }
};
class Stack2 : public Stack
{
public:
void push(int var)                                            // put number on stack
 {
if(top < MAX)                                    // if stack not full,               
        Stack::push(var);                       // call push( ) in Stack class else
        { cout << "\nError: stack is full";
           exit(1);
         }
}
 int pop( )                                                        // take number off stack
           {
if(top > 0)                                                        // if stack not empty,
           return Stack::pop( );                                         // call pop() in Stack class else
       { cout << "\nError: stack is empty"; exit(1); }
           }
};
void main( )
{
Stack2 51;
            s1.push(11);                                         // push some values onto stack s1.push(22);
s1.push(33);
cout << endl << s1.pop( );                  // pop some values from stack cout << endl << s1.pop( );
cout << endL << s1.pop( );
cout << endL << s1.pop( );      
}

 

7.8 Multilevel Inheritance:


It is not uncommon that a class is derived from another derived class as shown. The class A serves as a base class for the derived class B that 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.


 

Base class        A        Grandfather


 


 

Intermediate      B         Father
 base class


Child
 

Derived class     C      


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:

# include <iostream.h>
class student
{
protected:
int roll_number;
public:
void get-number(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 subl;
float sub2;
public:
void get-marks(float, float) ;
void put-marks(void) ;
};

void test :: get-marks(float x, float y)
{ subl = x;. sub2 = y; }

void test: : put-marks()
{
cout << "Marks in SUBl = " << 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 + sub2;
put_number ( ) ;
put-marks ( ) ;
cout << “Total= “ << total << "\n”;
}

main( )
 {
result student 1;                      //studentl created
studentl.get-number(lll) ;
studentl.get-marks(75.0, 59.5) ;
studentl.display( ) ;
}

7.9 Multiple Inheritance:

A class can inherit the attributes of two or more classes. 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 (papa) and the intelligence of another (mama). The syntax of a derived class with multiple base classes is as follows:

Class D : visibility B-1, visibility B-2,
{
…………..
………….. (Body of D)
…………..
};

where, visibility may be either public or private. The base classes are separated by commas. Example:

# include <iostream.h>
class M
{
protected:
int m;
public:
void get-m(int) ;
} ;
 class N
{
protected:
int n;
public:
void qet-n(int) ;
} ;
class P : public M, public N
{
public:
void display(void) ;
} ;
 void M ::get-m(int x)
{  m = x;          }
void N : : get-n(int y)
{          n = y;                         }
void P ::display(void)
{
cout << "m = " << m << "\n";
cout << "n = " << n << "\n";
cout << "m*n = " << m*n << “\n";
}
main( ) 
{
P p;
p.get-nl(10) ;
p.get-n(20) ;
p.display( ) ;
}

7.10 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. 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. Below:

             student










 



sports
 


test
 





result
 



# include <iostream.h> ,
class student
{
 protected:
 int roll-number;
  public:
void get-number(int a)
{          roll-number = a;           }
void put-number(void) 
{          cout << "Roll No: " << roll number << "\n"; }
} ;
class test: public student 
{
protected:
float partl, part2;
public:
void get_marks(float x, float y)
{          partl = x; part2 = y;    }
void put_marks(void) 
{         
cout << "Marks obtained: " << "\n” << “Fartl = " << partl << "\n” << “Fart2 =
“<< part2 << "\n" ;
            }
};
class sports 
{
protected:
float score;
public:
void get-score(float s)
{          score = s;        }
            void put-score(void)
{          cout << "Sports wt: " << score << "\n\n"; }
} ;
class result: public test, public sports
{
float total;
public:
void display(void) ;
};
void result :: display(void)
{
total = partl + part2 + score;
put_number ( ) ;
put_marks ( ) ;
put_score ( ) ;
cout << "Total Score: " << total << "\n";
}
main ( )
{
result student_l;
student_l. get_number (1234) ;
student_1.get_nlarks(27.5, 33.0) ;
student_1.get_score(6.0) ;
student_1.display( ) ;
}
 7.11 Containership: Classes Within Classes:

In inheritance, if a class B is derived from a class A, we can say "B is a kind of A". This is because B has all the characteristics of A, and in addition some of its own.  For this reason inheritance is sometimes called a "kind of” relationship:

There's another kind of relationship, called a "has a" relationship, or containership. In object-oriented programming the "has a" relationship occurs when one object is contained in another. Here's a case where an object of class B is contained in a class A:
class A
{
B b;                 // b is an object of class B
};
class B
{ };

In some situations inheritance and containership relationships can serve similar purposes. The following program shows these relationships in a different way:

# include<iostream.h>
const int LEN = 80;                            // maximum length of names
class student                                        // educational background
{
private:
char school[LEN];       // name of school or university
char degree[LEN];      // highest degree earned
            public:
void getedu( )
{
cout << “ Enter name of school or university: ";
cin >>school;
cout << " Enter highest degree earned \n";
cout << " (High school, Bachelor's, Master's, PhD): ";
cin >>degree;
}
void putedu( )
{
cout << "\n School or university: " << school;
cout << "\n Highest degree earned: " << degree;
}
};
class employee
{
private:
char name(LEN];                    // employee name
unsigned long number;            // employee number
public:
void getdata( )
{
cout << "\n Enter last name: ";
cin >>name;
cout << " Enter number: ";
cin >>number;
}
void putdata( )
{
cout << "\n Name: " << name;
cout << "\n Number: " << number;
}
 };

class manager                                                  // management
{
private:
char title(LEN];                       // "vice-president" etc.
double dues;                            // golf club dues
employee emp;                        // object of class employee
student stu;                             // object of class student
public:
void getdata( )
{
emp,getdata();
cout << " Enter title: ";                                    cin >>title;
cout << " Enter golf club dues: " ;      cin >>dues;
stu.getedu( );
}
 void putdata( )
{
emp.putdata( );
cout << "\n Title: " << title;
cout << "\n Golf club dues: " << dues;
stu.putedu( );
}
};

class scientist                                                   // scientist
{
private:
int pubs;                                  // number of publications
employee emp;                        // object of class employee
student stu;                             // object of class student
public:
void getdata( )
{
emp.getdata( );
cout << " Enter number of pubs: ";
cin >>pubs;
stu.getedu( );
}
void putdata()
{
emp.putdata( );
cout << "\n Number of publi cations: " << pubs;
s u.putedu( );
}
};
class laborer                                                     //laborer 
{
private:
employee emp;                        // object of class employee
public:
void getdata( )
{ emp.getdata( ); }
void putdata( )
{ emp.putdata( ); }
};
void main( )
{
manager m1;
scientist s1, s2;
laborer l1;
cout << endl;
cout << "\nEnter data for manager 1";                        // get data for
m1.getdata( );                                                  // several employees
cout << "\nEnter data for scientist 1";                        s1.getdata( );
cout << "\nEnter data for scientist 2";                        s2.getdata( );
cout << "\nEnter data for laborer 1";                          l1.getdata( );
cout << "\nData on manager 1";                                 m1.putdata( ); 
cout << "\nData on scientist 1";                                  s1.putdata( );
cout << "\nData on scientist 2";                                  s2.putdata( );
cout << "\nData on laborer 1";                                    l1.putdata();
}

Containership is clearly useful with classes that act like a data type. Then an object of that type can be used in a class in almost the same way a variable would be. In other situations you will need to examine the problem carefully and perhaps try different approaches to see what makes sense. Often the inheritance relationship is simpler to implement and offers a clearer conceptual framework.


















Exercise:
  1. The class master derives information from both accounts and admin classes, which in turn derive information from the class person. Define all four classes and write a program to create, update and display the information contained in master objects.
  2. Start with three classes Publication, Book and Tape, add a base class sales hat holds an array of three floats so that it can record the sales of a particular publication. Write a program to use these classes.
  3. Using the techniques of OOPs design a program that would simulate a simple real world system familiar to you