Unit 3
Unit 3
Inheritance
It is the process by which object of one class acquires the properties of object of another class. The class
from which properties are inherited is called base class and the class to which properties are inherited is
called derived class. Inheritance can be broadly classified into:
Single Inheritance
Multiple Inheritance
Multilevel Inheritance
Hierarchical Inheritance
Hybrid Inheritance
The access status of the base-class members inside the derived class is determined by access. The base-
class access specifier must be either public, private, or protected. If no access specifier is present, the
access specifier is private by default if the derived class is a class. If the derived class is a struct, then
public is the default in the absence of an explicit access specifier.
When the access specifier for a base class is public, all public members of the base become
public members of the derived class, and all protected members of the base become protected
members of the derived class.
When the base class is inherited by using the private access specifier, all public and protected
members of the base class become private members of the derived class.
When a base class' access specifier is protected, public and protected members of the base
become protected members of the derived class.
In all cases, the base's private elements remain private to the base and are not accessible by members
of the derived class.
Single Inheritance
In a single inheritance the derived class is derived from a single base class.
A
(Single inheritance)
Program 4.1 Example of single inheritance with base class access control as public.
Solution:
#include <iostream.h>
class A
{
int i, j;
public:
void set(int a, int b)
{
i=a; j=b;
}
void show()
{
cout << i << " " << j << "\n";
}
};
class B : public A
{
int k;
public:
B(int x)
{
k=x;
}
void showk()
{
cout << k << "\n";
}
};
void main()
{
B b(3);
b.set(1, 2);
b.show();
b.showk();
}
Output:
12
3
Note: Here all public and protected members of the base class become private members of the derived
class. So object of derived class cannot directly access the member function and data members of the
base class.
Program 4.2
Example of single inheritance with base class access control as private.
Solution:
#include <iostream.h>
class A
{
int i, j;
public:
void set(int a, int b)
{
i=a; j=b;
}
void show()
{
cout << i << " " << j << "\n";
}
};
class B : private A
{
int k;
public:
B(int x)
{
k=x;
}
void showk()
{
cout << k << "\n";
}
};
void main()
{
B b(3);
b.set(1, 2);
b.show(); //******Error******
}
How to access the private data member of base class in derived class?
Private data members of base class can be accessed by derived class by using public member
function/methods of the base class.
Multiple Inheritance
In multiple inheritance derived class is derived from more than one base class.
A B C
(Multiple Inheritance)
Multilevel Inheritance
In multilevel inheritance class B is derived from a class A and a class C is derived from the class B.
Syntax:
class base-class-name1
{
Data members
Member functions
};
Data members
Member functions
};
(Multilevel inheritance)
Hierarchical Inheritance
In hierarchical inheritance several classes can be derived from a single base class
Syntax:
class base-class-name
{
Data members
Member functions
};
Data members
Member functions
};
class derived-class-name2: visibility mode base-class-name
{
Data members
Member functions
};
B C D
(Hierarchical inheritance)
Hybrid inheritance
B
C
Solution:
#include <iostream.h>
class base
{
public:
base()
{
cout << "Constructing base\n";
}
~base()
{
cout << "Destructing base\n";
}
};
class derived: public base
{
public:
derived()
{
cout << "Constructing derived\n";
}
~derived()
{
cout << "Destructing derived\n";
}
};
void main()
{
derived ob;
}
Output:
Constructing base
Constructing derived
Destructing derived
Destructing base
Note: In the above program, first base's constructor is executed followed by derived's. Next (because ob
is immediately destroyed in this program), derived's destructor is called, followed by base's.
Program 4.5 Constructor and Destructor execution in multilevel linheritance.
Solution:
#include <iostream.h>
class base
{
public:
base()
{
cout << "Constructing base\n";
}
~base()
{
cout << "Destructing base\n";
}
};
class derived1 : public base
{
public:
derived1()
{
cout << "Constructing derived1\n";
}
~derived1()
{
cout << "Destructing derived1\n";
}
};
class derived2: public derived1
{
public:
derived2()
{
cout << "Constructing derived2\n";
}
~derived2()
{
cout << "Destructing derived2\n";
}
};
void main()
{
derived2 ob;
}
Output:
Constructing base
Constructing derived1
Constructing derived2
Destructing derived2
Destructing derived1
Destructing base
Solution:
#include <iostream.h>
class base1
{
public:
base1()
{
cout << "Constructing base1\n";
}
~base1()
{
cout << "Destructing base1\n";
}
};
class base2
{
public:
base2()
{
cout << "Constructing base2\n";
}
~base2()
{
cout << "Destructing base2\n";
}
};
class derived: public base1, public base2
{
public:
derived()
{
cout << "Constructing derived\n";
}
~derived()
{
cout << "Destructing derived\n";
}
};
void main()
{
derived ob;
}
Output:
Constructing base1
Constructing base2
Constructing derived
Destructing derived
Destructing base2
Destructing base1
Note: In the above program, constructors are called in order of derivation, left to right, as specified in
derived's inheritance list. Destructors are called in reverse order, right to left.
Here, base1 through baseN are the names of the base classes inherited by the derived class. Notice that
a colon separates the derived class' constructor declaration from the base-class specifications, and that
the base-class specifications are separated from each other by commas, in the case of multiple base
classes.
Program 4.7 Passing Parameters to Base Class Constructors in Single Inheritance.
Solution:
#include <iostream.h>
class base
{
protected:
int i;
public:
base(int x)
{
i=x; cout << "Constructing base\n";
}
~base()
{
cout << "Destructing base\n";
}
};
class derived: public base
{
int j;
public:
derived(int x, int y): base(y)
{
j=x; cout << "Constructing derived\n";
}
~derived()
{
cout << "Destructing derived\n";
}
void show()
{
cout << i << " " << j << "\n";
}
};
void main()
{
derived ob(3, 4);
ob.show();
}
Virtual Base Classes
An element of ambiguity can be introduced into a C++ program when multiple base classes are
inherited. For example, consider this incorrect program:
In Multipath Inheritance there is a one base class GRANDPARENT. Two derived class PARENT1 and
PARENT2 which are inherited from GRANDPARENT. Third Derived class CHILD which is inherited from
both PARENT1 and PARENT2.
GRAND PARENT
int i
PARENT1 PARENT 2
Program 4.7
Demonstration of ambiguities in multipath inheritance.
Solution:
/ This program contains an error and will not compile.
#include <iostream.h>
class base
{
public:
int i;
};
class derived1 : public base
{
public:
int j;
};
class derived2 : public base
{
public:
int k;
};
class derived3 : public derived1, public derived2
{
public:
int sum;
};
void main()
{
derived3 ob;
ob.i = 10; // this is ambiguous, which i???
ob.j = 20;
ob.k = 30;
ob.sum = ob.i + ob.j + ob.k; // i ambiguous here, too
cout << ob.i << " "; // also ambiguous, which i? cout
<< ob.j << " " << ob.k << " ";
cout << ob.sum;
}
As the comments in the program indicate, both derived1 and derived2 inherit base. However, derived3
inherits both derived1 and derived2. This means that there are two copies of base present in an object
of type derived3. Therefore, in an expression like
ob.i = 10; which i is being referred to, the one in derived1 or the one in derived2? Because there are two
copies of base present in object ob, there are two ob.is! As we can see, the statement is inherently
ambiguous.
There are two ways to remedy the preceding program. The first is to apply the scope resolution
operator to i and manually select one i. The second is to use virtual base class.
ob.i = 10;
ob.sum = ob.i + ob.j + ob.k;
cout << ob.i << " "; will be replaced by
ob.derived1::i = 10;
ob.sum = ob. derived1::i + ob.j + ob.k;
cout << ob. derived1::i << " "; respectively
As we can see, because the :: was applied, the program has manually selected derived1's version of
base. However, this solution raises a deeper issue: What if only one copy of base is actually required? Is
there some way to prevent two copies from being included in derived3? The answer, as you probably
have guessed, is yes. This solution is achieved using virtual base classes.
Program 4.8
Remove Ambiguities using virtual base class.
Solution:
#include <iostream.h>
class base
{
public:
int i;
};
class derived1 : virtual public base
{
public:
int j;
};
class derived2 : virtual public base
{
public:
int k;
};
class derived3 : public derived1, public derived2
{
public:
int sum;
};
void main()
{
derived3 ob;
ob.i = 10; // now unambiguous
ob.j = 20;
ob.k = 30;
ob.sum = ob.i + ob.j + ob.k; // unambiguous
cout << ob.i << " "; // unambiguous
cout << ob.j << " " << ob.k << " ";
cout << ob.sum;
}
As we can see, the keyword virtual precedes the rest of the inherited class specification. Now that both
derived1 and derived2 have inherited base as virtual, any multiple inheritance involving them will cause
only one copy of base to be present. Therefore, in derived3, there is only one copy of base and ob.i = 10
is perfectly valid and unambiguous.
Virtual destructor
In C++ a destructor is generally used to deallocate memory and do some other
cleanup for a class object and it’s class members whenever an object is
destroyed. Destructors are distinguished by the tilde, the ‘~’ that appears in
front of the destructor name. In order to define a virtual destructor, all you have
to do is simply add the keyword “virtual” before the tilde symbol.
The need for virtual destructors in C++ is best illustrated by some examples.
Let’s start by going through an example that does not use virtual destructors,
and then we will go through an example that does use virtual destructors. Once
you see the difference, you will understand why virtual destructors are needed.
Take a look at the code below to start out:
void main(){
{
Base *basePtr = new Derive();
delete basePtr;
}
Output:
Constructing Base
Constructing Derive
Destroying Base
Note: Based on the output above, we can see that the constructors get called in
the appropriate orderwhen we create the Derive class object pointer in the main
function. But there is a major problem with the code above: the destructor for
the "Derive" class does not get called at all when we delete ‘basePtr’. So, how
can we fix this problem?
Well, what we can do is make the base class destructor virtual, and that will
ensure that the destructor for any class that derives from Base (in our case, its
the "Derive" class) will be called.
Program 7.3 Example with a Virtual Destructor:
So, the only thing we will need to change is the destructor in the Base class and here’s
what it will look
like – note that we highlighted the part of the code where the virtual keyword has been
added in bold:
class Base
{
public:
Base()
{
cout<<"Constructing Base";
}
virtual ~Base()
{
cout<<"Destroying Base";
}
};
Output:
Constructing Base
Constructing Derive
Destroying Derive
Destroying Base
Note: Here the derived class destructor will be called before the base class. So,
now you’ve seen whywe need virtual destructors and also how they work. One
important design paradigm of class design is that if a class has one or more
virtual functions, then that class should also have a virtual destructor.
Classify the various types of polymorphism. conver
o Polymorphism means "one interface, sion
routin
Polymorphism multiple implementations" es
o Polymorphism is either static or dynamic o If
o In static or compile-time polymorphism, it does
the function code to be executed for a not
Static Dynamic
function call is known in advance, i.e., the result
binding is done early during compilation. in a
o Function overloading and Operator unique
Function Operator Virtual overloading belong to early binding. match
o In dynamic or run-time polymorphism the then
Overload Overload Function ambig
binding of function to a call is deferred
until runtime, i.e., dynamic or late binding. uity
o Runtime polymorphism is achieved error
is
through virtual functions.
report
ed.
#include <iostream.h>
const float pi = 3.14;
int volume(int); // cube volume
int volume(int, int, // box volume
int); // cylinder volume
float volume(int, int);
int main()
{
cout <<"Cube volume : "<< volume(5) <<"\n";
cout <<"Box volume : "<< volume(9, 3, 4) <<"\n";
cout <<"Cylinder volume : "<< volume(5, 6) <<"\n";
return 0;
}
int volume(int a)
{
return (a*a*a);
}
int volume(int l, int b, int h)
{
return (l * b * h);
}
float volume(int r, int h)
{
return (pi * r * r * h);
}