top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Inheritance in C++

0 votes
368 views

INHERITANCE

Inheritance is the process of creating new classes from an existing base class.

In order to maintain and reuse class easily, we should to be able to relate classes of similar nature with another. For example let us consider people employed in an organization. There are employees who perform different duties, for example the director, a manager, a secretary, and a clerk. Now each of these employees have some common features that link all of them. For example, all employees must have:

-->name

-->age

-->employee id

-->salary structure

-->department

These are the common properties they share. In addition to this, a director has certain number of people working under him, added responsibilities, and probably a different set of perks also. If we were to group the employees in different levels they could be categorized as level 1 for the director, level 2 for a manager, and so on. If we needed to put all this information together to form a class in an object-oriented program we would first consider a common class called Employee. This class can then be subdivided into classes such as Director, Manager, Secretary, and Clerk as shown below--

     Fig: Class Employee and its Subclasses

The basic idea behind inheritance is that in a class hierarchy, the derived classes inherit the methods and variables of the base class. In addition they can have properties and methods of their own.

            Fig: Derived Class Has Properties of Its Own

The class Manager that is derived from the class Employee inherits the common properties of name, age, employee id, salary and department. In addition, it has the data members perks and number of employees reporting, which are specific to the class Manager.

The most important advantage of inheritance is the reusability of code. Once a base class has been created it need not be changed but it can be adapt to work in different situations. One result of reusabilty of code is the development of class libraries.

Many vendors offer class libraries. A class library consists of data and methods encapsulated in a class. The source code of these class libraries need not be available to modify a class to suit one's needs. Modifying a class library does not require recompilation.

Deriving a class from an existing one allows redefining a member function of the base class and also adding new members to the derived class. This is possible without having the source code of the class definition. All that is required is the class library, which does not require recompilation. The base class remains unchanged in the process.

Base Class and Derived Class

Derivation can be represented graphically with an arrow from the derived class to the base class as shown below--

                     Fig: Manager Derived From Employee

The arrow in the diagram is meant to show that class Manager is derived from the base class Employee. The arrow pointing towards the base class signifies that the derived class refers to the functions and data in the base class, while the base class has no access to the derived class.

The declaration of a derived class is similar to that of any ordinary class except we also have to give the name of the base class. For example,

class Manager : public Employee

Any class can be used as a base class. This means that a derived class can in turn be the base for another class. Therefore, a base class can be classified into two types:

  >  direct base

  >  indirect base

A base class is called direct if it is mentioned in the base list. For example:

      class A

      {    };

     class B:public A

      {    };

    where class A is a direct class.An indirect class can be written as:

    class A

      {    };

    class B:public A

     {    };

    class C:public B

     {    };

Here B is derived from A and C from B. The class A is the indirect base class from C. This can be extended to an arbitrary number of levels.

Accessing Base Class Members

An important aspect of inheritance is knowing when a member function or data member of a base class can be used by objects of the derived class. This is called accessibility.

class members can always be accessed by member functions within their own class whether the members are private or public. Objects defined outside the class can access class members only if the members are public. For example, if emp1 is an instance of class Employee, and display() is a member function of Employee, then in main() the statement 

emp1.display();

is valid if dispaly() is public.

With inheritance, the member functions of the derived class can access members of the base class if its members are public. The derived class members cannot access the private members of the base class. There is one more section called the protected section. The protected section is like the private section in terms of scope and access that is, like private members, protected only members of that class can access members. Objects or functions from outside the class cannot access protected members within the class. This property of protected members is also similar to that of private members. However, the difference between them appears only in derived classes.

Private members of a class cannot be derived. Only public and protected members of a class can be derived. In other words members of the derived class can access public and protected members; they cannot access the private members of the base class. This restriction is in conformance with the object-oriented concept of information hiding. The designer of a class may not want anyone to have access to some of the class member and those members can be put in the private section. On the other hand, the designer can allow controlled access by providing some protected members.

Inheritance does not work in reverse. The base class and its objects will not know anything about any classes derived from it.

                  Fig: Access Rules For Base Class Members

Using the above rules in the table, let us look at an example.

class Employee { //base class

  private:

    int privA;

  protected:

    int protA;

  public:

    int pubA;

};

class Manager: public Employee { //derived class

  public:

    void fn()

  {

    int a;

    a = privA; //error:not accessible

    a = protA; // valid

    a = pubA; // valid

  }

};

void main()

{

  Employee emp;

  //Object of base class type

  emp.privA = 1; //error: not accessible

  emp.protA = 1; //error:not accessible 

  emp.pubA = 1;

  //valid 

  Manager mgr;

  //object of derived class

  mgr.privA = 1; //error:not accessible

  mgr.protA = 1; //error:not accessible

  mgr.pubA = 1; //valid

}

In the example, you can see that the function fn() can access the public and protected base class members from the derived class. However, in main() only the public base class member can be accessed.

While writing a class if you foresee it being used as a base class in the future then any data or functions that the derived class might need to access should be made protected rather than private.

Calling Member Functions

Member functions in a derived class can have the same nae as those in the base class. When the function is involved with the object of the base class, the function from the base class is called. When you use the name of the derived class object, the function from the derived class is invoked.

                            Fig: Accessibility For Derived Classes

If a member function from the derived class wants to cal the base class function of the same name, it must use the scope resolution operator as shown in the example

class Base

{

  protected:

    int ss;

  public:

    int func()

  {

    return ss;

  };

  public:

    int func()

  {

    return Base::func(); //calling base class func

  }

};

void main()

{

  Base b1; //base class object

  b1.func(); //calls base class func

  Derived a1; //derived class object

  a1.func(); //calls derived class func

}

The function in a base class can be invoked using the objects of the base class as well as the derived class. If a function exists in the derived class and not in the base class, it can be invoked only with the objects of the derived class. The object of the base class does not know anything about the derived class and will always use the base class functions only.  

Go through this video-:

References

Youtube.
posted Apr 26, 2017 by Pooja Singh

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

Liskov substitution principle aka known as LSP principle concept in object oriented design states that 

"Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it."

 

This principle was written by Barbara Liskov in 1988.

The idea here is that the subtypes must be replaceable for the super type references without affecting the program execution.

This principle is very closely related to Open Closed Principle(OCP), violation of LSP in turn violates the OCP. Let me explain:

If the subtype is not replaceable for the supertype reference, then in order to support the subtype instances as well we go ahead and make changes to the existing code and add the support. This is a clear violation of OCP.

This is mostly seen in places where we do run time type identification and then cast it to appropriate reference type. And if we add a new subtype implementation then we would have to edit the code to test for instance of for the new subtype.

Let me give a subtle example:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

class Bird {

  public void fly(){}

  public void eat(){}

}

class Crow extends Bird {}

class Ostrich extends Bird{

  fly(){

    throw new UnsupportedOperationException();

  }

}

 

public BirdTest{

  public static void main(String[] args){

    List<Bird> birdList = new ArrayList<Bird>();

    birdList.add(new Bird());

    birdList.add(new Crow());

    birdList.add(new Ostrich());

    letTheBirdsFly ( birdList );

  }

  static void letTheBirdsFly ( List<Bird> birdList ){

    for ( Bird b : birdList ) {

      b.fly();

    }

  }

}

What do you think would happen when this code is executed? As soon as an Ostrich instance is passed, it blows up!!! Here the sub type is not replaceable for the super type.

How do we fix such issues?

By using factoring. Sometimes factoring out the common features into a separate class can help in creating a hierarchy that confirms to LSP.

In the above scenario we can factor out the fly feature into- Flight and NonFlight birds.

 

 

 

 

 

 

 

class Bird{

  public void eat(){

}

}

class FlightBird extends Bird{

  public void fly()()

}

class NonFlight extends Bird{

}

So instead of dealing with Bird, we can deal with 2 categories of birds- Flight and NonFlight.

How can we identify LSP violation?

  • Derived class may require less functionalities than the Base class, so some methods would be redundant.
  • We might be using IS-A to check for Super-Sub relationships, but LSP doesn’t use only IS-A, but it also requires that the Sub types must be substitutable for the Super class. And one cannot decide the substitutability of sub class in isolation. One has to consider how the clients of the class hierarchy are going to use it.
READ MORE
...