8.
Inheritance: Extending
Classes
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 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, the traits of one class may be inherited by more
than one class. This process is known as hierarchical
inheritance. The mechanism of deriving a class from another
‘derived class’ is known as multilevel inheritance.
class derived-class-name : visibility-mode base-class-name
{
....//
....// members of derived class
....//
};
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.
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.
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. However, if the
derived class does not redefine the function, then the base
class function will be called.
We have 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.
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.
The keywords private, protected, and public may appear
in any order and any number of times in the declaration of a
class.
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.
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.(Not sure how and
whether it is true or not).
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.
Intermediate base class.
Inheritance path.
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.
Ambiguity Resolution in Inheritance.
Base class function whose name is same as that of a member
function in the derived class can be invoked by an object of
the derived class using scope resolution operator as follows:
b.A::display();
where b is object of derived class, A is the base class and
display is the function name which is present in both the
classes.
Hierarchical inheritance.
Hybrid inheritance.
The duplication of inherited members due to the multiple
paths can be avoided by making the common base class
(ancestor class) as virtual base class while declaring the
direct or intermediate base classes as shown below:
class A // grandparent
{
....
....
};
class B1 : virtual public A // parent1
{
....
....
};
class B2 : public virtual A // parent2
{
....
....
};
class C : public B1, public B2 // child
{
.... // only one copy of A
.... // will be inherited
};
When a class is made a virtual base class, C++ takes
necessary care to see that only one copy of that class is
inherited, regardless of how many inheritance paths exist
between the virtual base class and a derived class.
The keywords virtual and public may be used in either
order.
An abstract class is one that is not used to create objects. An
abstract class is designed only to act as a base class (to be
inherited by other classes). It is a design concept in program
development and provides a base upon which other classes
may be built.
However, one must never forget the fact that by definition a
class can only be considered as an abstract class if it has at
least one pure virtual function.
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. 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.
The constructors for virtual base classes are invoked before
any nonvirtual base classes. If there are multiple virtual base
classes, they are invoked in the order in which they are
declared. Any nonvirtual bases are then constructed before
the derived class constructor is executed.
C++ supports another method of initializing the class objects.
This method uses what is known as initialization list in the
constructor function. This takes the following form:
constructor (arglist) : intialization-section
{
assignment-section
}
The assignment-section is nothing but the body of the
constructor function and is used to assign initial values to its
data members. The part immediately following the colon is
known as the initialization section. We can use this section to
provide initial values to the base constructors and also to
initialize its own class members. This means that we can use
either of the sections to initialize the data members of the
constructors class. The initialization section basically contains
a list of initializations separated by commas. This list is
known as initialization list. Consider a simple example:
class XYZ
{
int a;
int b;
public:
XYZ(int i, int j) : a(i), b(2 * j) { }
};
main()
{
XYZ x(2, 3);
}
This program will initialize a to 2 and b to 6.
Remember, the data members are initialized in the order of
declaration, independent of the order in the initialization list.
A class can contain objects of other classes. This is known as
containership or nesting.
The constructors of the member objects are called in the
order in which they are declared in the nested class.
When the properties of one class are inherited by more than
one class, it is called hierarchical inheritance.