Unit 3
Unit 3
INHERITANCE
INHERITANCE
DEFINITION: Inheritance is a process in which a new class known as derived class is
created from another class called base class.
The access status of the base-class members inside the derived class is determined by
access. The base-class access specifier must be 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.
PROGRAM:
#include <iostream>
1
using namespace std;
class base
{
int i, j;
public:
void set (int a, int b)
{
i = a;
j = b;
}
void show ()
{
cout << i << " " << j << "\n";
}
};
int main ()
{
derived ob (3);
ob.set (1, 2); // access member of base
ob.show (); // access member of base
ob.showk (); // uses member of derived class
return 0;
}
OUTPUT:
12
3
2
PROGRAM:
#include <iostream>
using namespace std;
int main ()
{
derived ob (3);
ob.set (1, 2); // error, can't access set()
ob.show (); // error, can't access show()
return 0;
}
OUTPUT:
main.cpp: In function ‘int main()’:
main.cpp:48:15: error: ‘void base::set(int, int)’ is inaccessible within this context
ob.set (1, 2); // error, can't access set()
^
main.cpp:18:8: note: declared here
void set (int a, int b)
3
^~~
main.cpp:48:15: error: ‘base’ is not an accessible base of ‘derived’
ob.set (1, 2); // error, can't access set()
^
main.cpp:49:12: error: ‘void base::show()’ is inaccessible within this context
ob.show (); // error, can't access show()
^
main.cpp:23:8: note: declared here
void show ()
^~~~
main.cpp:49:12: error: ‘base’ is not an accessible base of ‘derived’
ob.show (); // error, can't access show()
^
PROGRAM:
#include <iostream>
using namespace std;
4
class derived:public base
{
int k;
public:
// derived may access base's i and j
void setk ()
{
k = i * j;
}
void showk ()
{
cout << k << "\n";
}
};
int main ()
{
derived ob;
ob.set (2, 3); // OK, known to derived
ob.show (); // OK, known to derived
ob.setk ();
ob.showk ();
return 0;
}
OUTPUT:
23
6
PROGRAM:
#include <iostream>
using namespace std;
5
cout << i << " " << j << "\n";
}
};
int main ()
{
derived ob;
// ob.setij(2, 3); // illegal, setij() is
// protected member of derived
ob.setk (); // OK, public member of derived
ob.showall (); // OK, public member of derived
// ob.showij(); // illegal, showij() is protected
// member of derived
return 0;
}
OUTPUT:
120 10 12
1. Single Inheritance
6
It is a process of creating new class called derived class from existing base class. The
derived class inherits the member functions and variables of the existing base class.
General form:
class derived-class-name : access base-class-name
{
// body of class
};
2. Multilevel Inheritance
When a derived class is used as a base class for another derived class, any protected
member of the base class that is inherited (as public) by the first derived class may also be
inherited as protected again by a second derived class.
PROGRAM:
#include <iostream>
using namespace std;
class base
{
protected:
int i, j;
public:
void set (int a, int b)
{
i = a;
j = b;
}
void show ()
{
cout << i << " " << j << "\n";
}
};
7
{
int k;
public:
void setk ()
{
k = i * j;
} // legal
void showk ()
{
cout << k << "\n";
}
};
int main ()
{
derived1 ob1;
derived2 ob2;
ob1.set (2, 3);
ob1.show ();
ob1.setk ();
ob1.showk ();
ob2.set (3, 4);
ob2.show ();
ob2.setk ();
ob2.setm ();
ob2.showk ();
ob2.showm ();
return 0;
}
OUTPUT:
23
6
34
12
8
-1
If, however, base were inherited as private, then all members of base would become
private members of derived1, which means that they would not be accessible by derived2.
(However, i and j would still be accessible by derived1.)
PROGRAM:
// This program won't compile.
#include <iostream>
using namespace std;
class base
{
protected:
int i, j;
public:
void set (int a, int b)
{
i = a;
j = b;
}
void show ()
{
cout << i << " " << j << "\n";
}
};
9
{
m = i - j;
} // Error
void showm ()
{
cout << m << "\n";
}
};
int main ()
{
derived1 ob1;
derived2 ob2;
ob1.set (1, 2); // error, can't use set()
ob1.show (); // error, can't use show()
ob2.set (3, 4); // error, can't use set()
ob2.show (); // error, can't use show()
return 0;
}
OUTPUT:
main.cpp: In member function ‘void derived2::setm()’:
main.cpp:53:9: error: ‘int base::i’ is protected within this context
m = i - j;
^
main.cpp:16:7: note: declared protected here
int i, j;
^
main.cpp:53:13: error: ‘int base::j’ is protected within this context
m = i - j;
^
main.cpp:16:10: note: declared protected here
int i, j;
^
main.cpp: In function ‘int main()’:
main.cpp:65:16: error: ‘void base::set(int, int)’ is inaccessible within this cont
ext
ob1.set (1, 2); // error, can't use set()
^
main.cpp:18:8: note: declared here
void set (int a, int b)
^~~
main.cpp:65:16: error: ‘base’ is not an accessible base of ‘derived1’
ob1.set (1, 2); // error, can't use set()
^
main.cpp:66:13: error: ‘void base::show()’ is inaccessible within this context
ob1.show (); // error, can't use show()
^
main.cpp:23:8: note: declared here
void show ()
^~~~
main.cpp:66:13: error: ‘base’ is not an accessible base of ‘derived1’
ob1.show (); // error, can't use show()
^
10
main.cpp:67:16: error: ‘void base::set(int, int)’ is inaccessible within this cont
ext
ob2.set (3, 4); // error, can't use set()
^
main.cpp:18:8: note: declared here
void set (int a, int b)
^~~
main.cpp:67:16: error: ‘base’ is not an accessible base of ‘derived2’
ob2.set (3, 4); // error, can't use set()
^
main.cpp:68:13: error: ‘void base::show()’ is inaccessible within this context
ob2.show (); // error, can't use show()
^
main.cpp:23:8: note: declared here
void show ()
^~~~
main.cpp:68:13: error: ‘base’ is not an accessible base of ‘derived2’
ob2.show (); // error, can't use show()
3. Multiple Inheritance
It is possible for a derived class to inherit two or more base classes. Multiple
inheritance allows us to combine the features of several existing classes as a starting point for
defining new class.
General form:
class derived-class-name : access base-class-name1, access base-class-name2
{
// body of class
};
PROGRAM:
#include <iostream>
using namespace std;
class base1
{
protected:
int x;
public:
void showx ()
{
cout << x << "\n";
}
};
11
class base2
{
protected:
int y;
public:
void showy ()
{
cout << y << "\n";
}
};
int main ()
{
derived ob;
ob.set (10, 20); // provided by derived
ob.showx (); // from base1
ob.showy (); // from base2
return 0;
}
OUTPUT:
10
20
4. Hierarchical Inheritance
In this more than one class are derived from a single base class. This supports
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 the level.
PROGRAM:
#include<iostream>
12
using namespace std;
int main ()
{
B obj1; //object of derived class B
C obj2; //object of derived class C
obj1.getdata ();
obj1.product ();
obj2.getdata ();
obj2.sum ();
return 0;
} //end of program
OUTPUT:
Enter value of x and y:
23 45
Product= 1035
13
5. Hybrid Inheritance
It involves more than one form of any inheritance i.e. we apply two or more types of
inheritance as one.
PROGRAM:
#include <iostream>
using namespace std;
class A
{
public:
int x;
};
class B:public A
{
public:
B () //constructor to initialize x in base class A
{
x = 10;
}
};
class C
{
public:
int y;
C () //constructor to initialize y
{
y = 4;
}
};
OUTPUT:
Sum= 14
6. Multipath Inheritance
It is a derivation of a class from other derived classes, which are derived from the
same base class. This type of inheritance involves other inheritance like multiple, multilevel,
hierarchical etc.
PROGRAM:
#include <iostream>
using namespace std;
class person
{
public:
char name[100];
int code;
void input()
{
cout<<"\nEnter the name of the person : ";
cin>>name;
cout<<endl<<"Enter the code of the person : ";
cin>>code;
}
void display()
{
cout<<endl<<"Name of the person : "<<name;
cout<<endl<<"Code of the person : "<<code;
}
};
15
class account:virtual public person
{
public:
float pay;
void getpay()
{
cout<<endl<<"Enter the pay : ";
cin>>pay;
}
void display()
{
cout<<endl<<"Pay : "<<pay;
}
};
}
void display()
{
cout<<endl<<"Experience : "<<experience;
}
};
int main ()
{
master m1;
16
m1.input();
m1.getpay();
m1.getexp();
m1.gettotal();
cout<<"Displaying Information";
m1.person::display();
m1.account::display();
m1.admin::display();
m1.display();
return 0;
} //end of program
OUTPUT:
Enter the name of the person : asd
Enter the code of the person : 1234
Enter the pay : 20000
Enter the experience : 2
Enter the company name : cmr
Displaying Information
Name of the person : asd
Code of the person : 1234
Pay : 20000
Experience : 2
Company name : cmr
PROGRAM:
#include <iostream>
17
using namespace std;
class base
{
public:
base ()
{
cout << "Constructing base\n";
}
~base ()
{
cout << "Destructing base\n";
}
};
int main ()
{
derived2 ob;
// construct and destruct ob
return 0;
} //end of program
OUTPUT:
Constructing base
18
Constructing derived1
Constructing derived2
Destructing derived2
Destructing derived1
Destructing base
The same general rule applies in situations involving multiple base classes.
PROGRAM:
#include <iostream>
using namespace std;
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";
}
};
19
int main ()
{
derived ob;
// construct and destruct ob
return 0;
} //end of program
OUTPUT:
Constructing base1
Constructing base2
Constructing derived
Destructing derived
Destructing base2
Destructing base1
PROGRAM:
#include <iostream>
using namespace std;
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";
}
};
20
{
public:
derived ()
{
cout << "Constructing derived\n";
}
~derived ()
{
cout << "Destructing derived\n";
}
};
int main ()
{
derived ob;
// construct and destruct ob
return 0;
} //end of program
OUTPUT:
Constructing base2
Constructing base1
Constructing derived
Destructing derived
Destructing base1
Destructing base2
base1 through baseN are the names of the base classes inherited by the derived class.
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:
#include <iostream>
21
using namespace std;
class base
{
protected:
int i;
public:
base (int x)
{
i = x;
cout << "Constructing base\n";
}
~base ()
{
cout << "Destructing base\n";
}
};
int main ()
{
derived ob (3, 4);
ob.show (); // displays 4 3
return 0;
}
OUTPUT:
Constructing base
Constructing derived
43
Destructing derived
22
Destructing base
The derived class' constructor must declare both the parameter(s) that it requires as
well as any required by the base class.
PROGRAM:
#include<iostream>
using namespace std;
class base1
{
protected:
int i;
public:
base1 (int x)
{
i = x;
cout << "Constructing base1\n";
}
~base1 ()
{
cout << "Destructing base1\n";
}
};
class base2
{
protected:
int k;
public:
base2 (int x)
{
k = x;
cout << "Constructing base2\n";
}
~base2 ()
{
cout << "Destructing base1\n";
}
};
23
}
~derived ()
{
cout << "Destructing derived\n";
}
void show ()
{
cout << i << " " << j << " " << k << "\n";
}
};
int main ()
{
derived ob (3, 4, 5);
ob.show (); // displays 4 3 5
return 0;
}
OUTPUT:
Constructing base1
Constructing base2
Constructing derived
435
Destructing derived
Destructing base1
Destructing base1
Arguments to a base-class constructor are passed via arguments to the derived class'
constructor. Therefore, even if a derived class' constructor does not use any arguments, it will
still need to declare one if the base class requires it. In this situation, the arguments passed to
the derived class are simply passed along to the base.
PROGRAM:
#include<iostream>
using namespace std;
class base1
{
protected:
int i;
public:
base1 (int x)
{
i = x;
cout << "Constructing base1\n";
}
~base1 ()
{
cout << "Destructing base1\n";
}
24
};
class base2
{
protected:
int k;
public:
base2 (int x)
{
k = x;
cout << "Constructing base2\n";
}
~base2 ()
{
cout << "Destructing base2\n";
}
};
int main ()
{
derived ob (3, 4);
ob.show (); // displays 3 4
return 0;
}
OUTPUT:
Constructing base1
Constructing base2
Constructing derived
34
25
Destructing derived
Destructing base2
Destructing base1
A derived class' constructor is free to make use of any and all parameters that it is
declared as taking, even if one or more are passed along to a base class. Put differently,
passing an argument along to a base class does not preclude its use by the derived class as
well. For example, this fragment is perfectly valid:
class derived: public base
{
int j;
public:
// derived uses both x and y and then passes them to base.
derived(int x, int y): base(x, y)
{ j = x*y; cout << "Constructing derived\n"; }
One final point to keep in mind when passing arguments to base-class constructors:
The argument can consist of any expression valid at the time. This includes function calls and
variables. This is in keeping with the fact that C++ allows dynamic initialization.
PROGRAM:
// This program contains an error and will not compile.
#include <iostream>
using namespace std;
class base
{
public:
int i;
};
/* derived3 inherits both derived1 and derived2. This means that there are two copies of base
in derived3! */
26
class derived3:public derived1, public derived2
{
public:
int sum;
};
int main ()
{
derived3 ob;
ob.i = 10; // this is ambiguous, which i???
ob.j = 20;
ob.k = 30;
// i ambiguous here, too
ob.sum = ob.i + ob.j + ob.k;
// also ambiguous, which i?
cout << ob.i << " ";
cout << ob.j << " " << ob.k << " ";
cout << ob.sum;
return 0;
}
OUTPUT:
main.cpp: In function ‘int main()’:
main.cpp:40:6: error: request for member ‘i’ is ambiguous
ob.i = 10; // this is ambiguous, which i???
^
main.cpp:14:7: note: candidates are: int base::i
int i;
^
main.cpp:14:7: note: int base::i
main.cpp:44:15: error: request for member ‘i’ is ambiguous
ob.sum = ob.i + ob.j + ob.k;
^
main.cpp:14:7: note: candidates are: int base::i
int i;
^
main.cpp:14:7: note: int base::i
main.cpp:46:14: error: request for member ‘i’ is ambiguous
cout << ob.i << " ";
^
main.cpp:14:7: note: candidates are: int base::i
int i;
^
main.cpp:14:7: note: int base::i
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;
27
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!, the statement is
inherently ambiguous.
Solutions:
There are two ways to remedy the preceding program.
1. Apply the scope resolution operator to i and manually select one i.
PROGRAM:
// This program uses explicit scope resolution to select i.
#include <iostream>
using namespace std;
class base {
public:
int i;
};
/* derived3 inherits both derived1 and derived2. This means that there are two copies of base
in derived3! */
class derived3 : public derived1, public derived2 {
public:
int sum;
};
int main()
{
derived3 ob;
ob.derived1::i = 10; // scope resolved, use derived1's i
ob.j = 20;
ob.k = 30;
// scope resolved
ob.sum = ob.derived1::i + ob.j + ob.k;
// also resolved here
cout << ob.derived1::i << " ";
cout << ob.j << " " << ob.k << " ";
cout << ob.sum;
return 0;
28
}
OUTPUT:
10 20 30 60
This solution raises a deeper issue: What if only one copy of base is actually
required? Preventing two copies from being included in derived.
++
2. Virtual Base Class
When two or more objects are derived from a common base class, you can prevent
multiple copies of the base class from being present in an object derived from those objects
by declaring the base class as virtual when it is inherited. You accomplish this by preceding
the base class' name with the keyword virtual when it is inherited.
PROGRAM:
// This program uses virtual base classes.
#include <iostream>
using namespace std;
class base
{
public:
int i;
};
/* derived3 inherits both derived1 and derived2. This time, there is only one copy of base
class. */
class derived3:public derived1, public derived2
{
public:
int sum;
};
int main ()
{
derived3 ob;
29
ob.i = 10; // now unambiguous
ob.j = 20;
ob.k = 30;
// unambiguous
ob.sum = ob.i + ob.j + ob.k;
// unambiguous
cout << ob.i << " ";
cout << ob.j << " " << ob.k << " ";
cout << ob.sum;
return 0;
}
OUTPUT:
10 20 30 60
30
VIRTUAL FUNCTIONS AND POLYMORPHISM
POLYMORPHISM
Object-oriented programming languages support polymorphism, which is
characterized by the phrase "one interface, multiple methods." In simple terms,
polymorphism is the attribute that allows one interface to control access to a general class of
actions. The specific action selected is determined by the exact nature of the situation.
Polymorphism refers to the ability to associate multiple meanings to one function
name.
Polymorphism is supported by C++ both at compile time and at run time. Compile
time polymorphism is achieved by overloading functions and operators. Run-time
polymorphism is accomplished by using inheritance and virtual functions
VIRTUAL FUNCTIONS
A virtual function is a member function that is declared within a base class and
redefined by a derived class. To create a virtual function, precede the function's declaration in
the base class with the keyword virtual. When a class containing a virtual function is
inherited, the derived class redefines the virtual function to fit its own needs. In essence,
virtual functions implement the "one interface, multiple methods" philosophy that underlies
polymorphism. The virtual function within the base class defines the form of the interface to
that function. Each redefinition of the virtual function by a derived class implements its
operation as it relates specifically to the derived class. That is, the redefinition creates a
specific method.
31
A base-class pointer can be used to point to an object of any class derived from that
base. When a base pointer points to a derived object that contains a virtual function, C++
determines which version of that function to call based upon the type of object pointed to by
the pointer. And this determination is made at run time. Thus, when different objects are
pointed to, different versions of the virtual function are executed. The same effect applies to
base-class references.
PROGRAM:
#include <iostream>
using namespace std;
class base
{
public:
virtual void vfunc ()
{
cout << "This is base's vfunc().\n";
}
};
int main ()
{
base *p, b;
derived1 d1;
derived2 d2;
// point to base
p = &b;
p->vfunc (); // access base's vfunc()
// point to derived1
p = &d1;
p->vfunc (); // access derived1's vfunc()
// point to derived2
32
p = &d2;
p->vfunc (); // access derived2's vfunc()
return 0;
}
OUTPUT:
This is base's vfunc().
This is derived1's vfunc().
This is derived2's vfunc().
33
PROGRAM:
/* Here, a base class reference is used to access a virtual function. */
#include <iostream>
using namespace std;
class base
{
public:
virtual void vfunc ()
{
cout << "This is base's vfunc().\n";
}
};
int main ()
{
base b;
derived1 d1;
derived2 d2;
f (b); // pass a base object to f()
f (d1); // pass a derived1 object to f()
f (d2); // pass a derived2 object to f()
return 0;
}
OUTPUT:
This is base's vfunc().
This is derived1's vfunc().
34
This is derived2's vfunc().
Definition: A pure virtual function is a virtual function that has no definition within the base
class.
General form:
virtual type func-name(parameter-list) = 0;
When a virtual function is made pure, any derived class must provide its own
definition. If the derived class fails to override the pure virtual function, a compile-time error
will result.
PROGRAM:
#include <iostream>
using namespace std;
class number
{
protected:
int val;
public:
void setval (int i)
{
val = i;
}
// show() is a pure virtual function
virtual void show () = 0;
};
35
{
cout << val << "\n";
}
};
int main ()
{
dectype d;
hextype h;
octtype o;
d.setval (20);
d.show (); // displays 20 - decimal
h.setval (20);
h.show (); // displays 14 - hexadecimal
o.setval (20);
o.show (); // displays 24 - octal
return 0;
}
OUTPUT:
20
14
24
ABSTRACT CLASSES
A class that contains at least one pure virtual function is said to be abstract. Because
an abstract class contains one or more functions for which there is no definition (that is, a
pure virtual function), no objects of an abstract class may be created. Instead, an abstract
class constitutes an incomplete type that is used as a foundation for derived classes.
Although you cannot create objects of an abstract class, you can create pointers and
references to an abstract class. This allows abstract classes to support run-time
polymorphism, which relies upon base-class pointers and references to select the proper
virtual function.
PROGRAM:
// C++ Program to Illustrate Abstract Class
#include <iostream>
using namespace std;
class Abstract
{
36
int i, j;
public:
virtual void setData (int i = 0, int j = 0) = 0;
virtual void printData () = 0;
};
int main ()
{
// Cannot create an instance of Abstract Class
// Abstract a;
Derived d;
OUTPUT:
Creating object
Current data
Derived::i = 0
Derived::j = 0
New data
Derived::i = 10
Derived::j = 20
VIRTUAL DESTRUCTORS
Inheritance also lends itself to virtual methods, where implementation is provided by
any specific subclasses. However, once an inheritance hierarchy is created, with memory
37
allocations occurring at each stage in the hierarchy, it is necessary to be very careful about
how objects are destroyed so that any memory leaks are avoided. In order to achieve this, we
make use of a virtual destructor.
In simple terms, a virtual destructor ensures that when derived subclasses go out of
scope or are deleted the order of destruction of each class in a hierarchy is carried out
correctly. If the destruction order of the class objects is incorrect, in can lead to what is
known as a memory leak. This is when memory is allocated by the C++ program but is never
deallocated upon program termination. This is undesirable behaviour as the operating system
has no mechanism to regain the lost memory (because it does not have any references to its
location!). Since memory is a finite resource, if this leak persists over continued program
usage, eventually there will be no available RAM (random access memory) to carry out other
programs.
For instance, consider a pointer to a base class (such as PayOff) being assigned to a
derived class object address via a reference. If the object that the pointer is pointing to is
deleted, and the destructor is not set to virtual, then the base class destructor will be called
instead of the derived class destructor. This can lead to a memory leak. Consider the
following code:
class Base
{
public:
Base();
~Base();
};
void do_something() {
Base* p = new Derived;
// Derived destructor not called!!
delete p;
}
What is happening here? Firstly, we create a base class called Base and a subclass
called Derived. The destructors are NOT set to virtual. In our do_something() function, a
pointer p to a Base class is created and a reference to a new Derived class is assigned to it.
This is legal as Derived is a Base.
However, when we delete p the compiler only knows to call Base's destructor as the pointer is
pointing to a Base class. The destructor associated with Derived is not called and val is not
deallocated.
A memory leak occurs!
Now consider the amended code below. The virtual keyword has been added to the
destructors:
class Base {
public:
38
Base();
virtual ~Base();
};
void do_something() {
Base* p = new Derived;
// Derived destructor is called
delete p;
}
What happens now? Once do_something() is called, delete is invoked on the
pointer p. At code execution-time, the correct destructor is looked up in an object known as
a vtable. Hence the destructor associated with Derived will be called prior to a further call to
the destructor associated with Base. This is the behaviour we originally desired. val will be
correctly deallocated.
No memory leak this time!
39