Inheritance is the process of extending the data and functions of an existing Class (base class) to a new Class (derived class). The derived class can also contain its own member-data and member-functions. The base class can be referred to as parent/super class. The derived class is also called child class.
Inheritance is one of the cornerstones of OOP because it allows the creation of hierarchical classifications. Using inheritance, you can create a general class that defines traits common to a set of related items. This class may then be inherited by other, more specific classes, each adding only those things that are unique to the inheriting class. In keeping with standard C++ terminology, a class that is inherited is referred to as a base class or super class. While, a class that does the inheriting is called the derived class.
Furthermore, a derived class can be used as a base class for another derived class. In this way, multiple inheritance is achieved. Support of inheritance in C++ is both rich and flexible.
TYPES OF INHERITANCE
1. Single Inheritance ( only one super class )
BASE CLASS ACCESS CONTROL
When a class inherits another class, members of the base class become members of the derived class. Class inheritance uses this general form:
class derived-class-name : access-scope base-class-name
{
/* body of class */
};
INHERITANCE AND PRIVATE/PUBLIC
MEMBERS
The access-scope of base-class members inside the derived class is determined by access specifiers: public, private, or protected. If no access specifier is present, then the access specifier is private by default. “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 class become public members of the derived class, and all protected members of the base class become protected members of the derived class. Private member elements remain private to the base and are not accessible by members of the derived class.
PROGRAM 67
Q. Write a program using inheritance to create a derived class that assigns member data (integer value) to itself and prints it using its own functions. Also assign integer member data to its base class and print it using the function in base class.
#include <iostream> using namespace std; class base { int i, j ; public: void set(int a,
int b) {
i=a; j=b; } void show() { cout << "Values in Base Class\n"; cout << "i =
"<<i<<"\t j = "<<j<<"\n\n"; } }; class derived : public base { int k ; public: derived(int x) { k=x; } void show_k() { cout << "Values in Derived Class\n"; cout << "k =
"<<k; } }; main ( ) { ob.set(10, 20);
// assign values to base class derived
ob(30); //assign value to derived class ob.show(); // print members of base class ob.show_k(); // print member of derived class } |
The protected keyword is included in C++ to provide greater flexibility in the inheritance mechanism. When the member of a Class is declared as Protected, that member is accessible by all members of the Class, but is not accessible by non-member elements of the program. However, a Protected member can be accessed within its derived (child) class. By using access specifier “protected”, you can create class members that are private to their class, but they can still be inherited and accessed by a derived class.
As explained in the preceding section, a private member of a base class is not accessible by other parts of your program, including any derived class. However, protected members behave differently. If the base class is inherited as public, then protected members of the base class become protected members of the derived class and are, therefore, accessible by the derived class. Here is an example.
PROGRAM 68
Q. Write a
program using inheritance to create a derived class that assigns integer values
into protected members of its base class and prints them using base class
functions. Multiply these same integer values of the base class and print the
result using functions of the derived class.
#include <iostream>
using namespace std;
class base
{
protected:
int
i, j; // private to base class, but
accessible by derived class
public:
void
set (int a, int b) { i=a; j=b; }
void
show()
{
cout << "Values in Base Class\n";
cout << "i = "<<i<<"\t j =
"<<j<<"\n\n";
}
};
class derived : public base
{
int
k;
public:
//
derived class can access i and j of base class
void
setk()
{
k=i*j; }
void
show_k()
{
cout << "Values Multiplied in Derived Class\n";
cout << "k = i X j =
"<<k;
}
} ;
main()
{
derived
ob;
ob.set(10,
20); // OK… of base class & known to
derived class
ob.show(); // OK… of base class & known to
derived class
ob.setk()
; // OK… of derived class
ob.
show_k() ; // OK… of derived class
}
OUTPUT
In this example, because the base class is inherited by derived class as public and because i and j are declared as protected, the derived Class function setk() is able to access them. If i and j had been declared as private by base, then derived Class would not have access to them, and the program would not compile.
When a derived class is used as a base class for another derived class, any protected member of the initial base class that is inherited (as public) by the first derived class may also be inherited as protected again by a second derived class.
For
example, in the below given program, second level derived Class named “derived2”
does have access to i and j.
PROGRAM 69
Q. Write a
program using inheritance to create a derived Class that assigns integer values
into protected members of its base class and prints them using base class
functions. Multiply these integer values of the base class and print the result
using functions of the derived class. Create a multi-level inherited Class from
the existing derived class. Assign new values to base class and print them
using base class function. Next, multiply and print them using first-level-derived
class function. Then, add and print them using member functions of the
second-level-derived class.
#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 << "Values in Base Class\n";
cout
<< "i = "<<i<<"\t j =
"<<j<<"\n\n";
}
};
// i and j inherited from Class ‘base’ by using public access
class derived1 : public
base
{
int k;
public:
void
setk()
{ k =
i*j; } // accessing i and j are allowed here
void
show_k()
{
cout
<< "Values Multiplied in Derived Class\n";
cout << "k = i X j =
"<<k<<"\n\n";
}
};
// i and j inherited
indirectly through Class ‘derived1’
class derived2 : public derived1
{
int
m;
public:
void
setm() { m = i+j; } // accessing i and j are allowed here
void
show_m()
{
cout
<< "Values Added in Multi-level Derived Class\n";
cout << "m = i + j =
"<<m;
}
};
main()
{
derived1 ob1;
derived2 ob2 ;
ob1.set (2, 3);
ob1.show( ) ;
ob1.setk( ) ;
ob1.show_k( ) ;
ob2.set(3, 4);
ob2 .show( ) ;
ob2.setk( ) ;
ob2.show_k ( ) ;
ob2.setm( ) ;
ob2.show_m( ) ;
It is possible for a derived class to inherit two or more base classes. In the following example program, derived class inherits both base1 and base2.
PROGRAM 70
Q. Write a program to create 2 classes with
one integer member and a print value function within each of them. Create a
child class that inherits both the above base classes. Assign values to the
integer variable of both base classes and print their corresponding values
using function in the derived class.
// An example of multiple inheritance
#include <iostream>
using namespace std;
class A
{
protected:
int x;
public:
void showx()
{
cout<<"x in Class A : ";
cout
<< x<< "\n";
}
};
class B
{
protected:
int y;
public :
void showy()
{
cout<<"y in Class B : ";
cout
<< y<< "\n";
}
};
// inheriting multiple
base classes
class C: public A, public B
{
public:
void set(int i,
int j)
{ x=i; y=j ; }
};
main ( )
{
C ob;
ob.set(10, 20); // provided by derived class C
ob.showx() ; // from class A
ob.showy(); // from class B
Suppose moveAhead() is a C++ function to move any object. Then,
- for a Car, the moveAhead() would mean to “Drive”,
- for a Ship, the moveAhead() would mean to “Sail”,
- for an Aeroplane, moveAhead() would mean to “Fly”,
- for a Bicycle, the moveAhead() would mean to “Pedal”.
So basically, when different objects (car, ship, aeroplane, bicycle) call and execute the same moveAhead() function, it would actually perform different operational methods (drive, sail, fly, pedal) to perform the task of moving ahead.
Types of Polymorphism
There are two types of polymorphism:
(i)
Static polymorphism or Compile time polymorphism
(ii)
Dynamic polymorphism or Run time polymorphism
Static Polymorphism is exhibited by overloaded functions. Static
polymorphism is the technique of overriding a function by providing additional
definitions with different numbers or types
of parameters. The compiler
matches the parameter list to the appropriate function that is called in the
program. (Remember how we had used function polymorphism in the chapter on
Functions). Here you declare and define
multiple functions with same name and
different parameters. For example, consider the following function
declarations:
void add(int , int);
void add(float, float);
Dynamic Polymorphism is exhibited by using late binding and is achieved by choosing correct “function-call” based on the “type of object” pointed by the pointer. A function is said to exhibit dynamic polymorphism when it exists in more than one form, and “calls” to its various forms are decided and executed dynamically when the program is executed.
In Dynamic polymorphism, there is a parent class having a declared and (or) defined virtual function. This virtual function can be redefined (rewritten) in a child class. At run-time, using appropriate Objects, you can either use the function within the parent class or within the child class.
The term late binding refers to the decision on using the required function (parent or child) at run-time instead of at compile-time. This feature increases the flexibility of the program by allowing the appropriate method (function) to be invoked (called).
For better understanding of this section, you need to know inheritance and must have a proper understanding of how the following works:
- pointer to class
- pointer to base class
- virtual members
We can create pointers that point to
classes by using the class name as the type for the pointer. For example: CRectangle * prect;
Here, prect is a pointer to an Object
of Class CRectangle
In order to refer directly to a member of an object pointed by a pointer, we can use the arrow operator (->) of indirection. Here is an example with some possible combinations.
PROGRAM 71
Q. Create a class CRectangle. Assign values
and print the area of corresponding rectangles. Use pointers to classes (note the
different ways we have used here as references to a Class). Declare the following
class members:
Member Data: height, width
Member Functions: void set_values
(int, int), int area (void)
#include <iostream>
using namespace std;
class CRectangle
{
int
width, height;
public:
void
set_values (int, int);
int
area (void)
{
return (width * height);
}
};
void CRectangle::set_values (int a, int b)
{
width = a;
height = b;
}
main ()
{
CRectangle A, *B, *C; // *B, *C are
pointers to Class
CRectangle *D = new CRectangle[2]; // pointer to
array of Class
B= new CRectangle;
C= &A;
A.set_values (10,20);
B->set_values (30,40);
D->set_values (50,60); // assigns values to D[0]
D[1].set_values (70,80); // assigns values to D[1]
cout <<
"Area of Class A : " << A.area() <<
endl;
cout << "Area of Class pointed by
*B : " << B->area() << endl;
cout << "Area of Class pointed by
*C : " << C->area() << endl;
cout << "Area of Class Array
D[0] : " << D[0].area()
<< endl;
cout << "Area of Class Array
D[1] : " << D[1].area()
<< endl;
}
OUTPUT
TRY IT YOURSELF In
main function of the above program, delete these 3 lines: CRectangle A, *B, *C; B = new CRectangle; C =
&A; Then
add these 2 lines: CRectangle A, *C = &A; CRectangle *B = new CRectangle; Q: Will it work? Try to understand memory allocation process here. |
A pointer that is set to the base (parent) class can be used to access the derived (child) class. One of the key features of derived classes is that a pointer to the derived class is type-compatible with a pointer to its base-class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature that brings Object Oriented Methodologies to its full potential. Here’s an example program.
#include <iostream> using namespace std; class CPolygon { protected: int
width, height; public: void
set_values (int a, int b) {
width=a; height=b; } }; class CRectangle: public CPolygon { public: int
area () { return (width * height); } }; class CTriangle: public CPolygon { public: int area () { return (width * height / 2); } }; main () {
CRectangle rect;
CTriangle trng;
CPolygon *ppoly1 = ▭
CPolygon *ppoly2 = &trng;
ppoly1 -> set_values (4,5);
ppoly2 -> set_values (4,5); cout<<"Area of Rectangle: "<<rect.area()<<"
sq.units"<<endl; cout<<"Area of Triangle : "<<trng.area()<<"
sq.units"<<endl; } |
OUTPUT
In the main function, we create two pointers that point to objects of base class CPolygon (ppoly1, ppoly2). Then we assign these two pointers with references to objects of derived classes (rect, trng). Since both these objects belong to classes that are derived from CPolygon, you can assign their address to the pointer of parent class.
The only
limitation in using *ppoly1 and *ppoly2 instead of rect and trng is that both *ppoly1
and *ppoly2 are of type CPolygon and therefore we can only use these pointers
to refer to the members that CRectangle and CTriangle inherit from CPolygon. For
that reason when we call the area() we have to use the objects rect and trng
instead of the pointers *ppoly1 and *ppoly2.
In order to use area() with the pointers to class CPolygon, this member should also have been declared in the class CPolygon. But area() for Rectangle is different from area() for Triangle and therefore we cannot implement it in the base class. This is when virtual members become important. You can declare area() as virtual in the base class and then refer to area() using the pointer to base class (in previous program, the base class pointer points to derived class address).
Virtual Members
A member of any Class, which can be redefined in its Derived Classes, is known as a virtual member. In order to declare a member of a class as virtual, we must precede its declaration with the keyword virtual. A virtual member provides runtime polymorphism.
#include
<iostream>
using
namespace std;
class
CPolygon
{
protected:
int width, height;
public:
void set_values (int a, int b)
{ width=a; height=b; }
virtual int area ()
{ return (0); }
};
class
CRectangle: public CPolygon
{
public:
int area ()
{ return (width * height); }
};
class
CTriangle: public CPolygon
{
public:
int area ()
{ return (width * height
/ 2); }
};
main
()
{
CPolygon poly; CRectangle rect; CTriangle
trgl;
CPolygon *ppoly1 = ▭
CPolygon *ppoly2 = &trgl;
CPolygon *ppoly3 = &poly;
ppoly1 -> set_values (4,5);
ppoly2 -> set_values (4,5);
ppoly3 -> set_values (4,5);
cout <<
ppoly1 -> area() << endl;
cout << ppoly2 -> area() <<
endl;
cout << ppoly3 -> area() <<
endl;
}
20 10 0 |
Abstract Class is a special base class which contains at least one pure virtual function.
The pure virtual function is a function that is only declared in the base class, and is defined in the derived classes.
We can declare a pure virtual function by using a pure specifier (= 0) in the declaration of a virtual member function in the Class declaration.
Abstract base classes are very similar to the class CPolygon of the previous example. The only difference is that in our previous example we defined a valid area() within the base Class, with a minimal functionality for objects that were of class CPolygon (like the object poly), whereas in an abstract base class we would leave area() without any definition (implementation details). This is done by appending =0 (equal to zero) to the function declaration. An abstract base CPolygon class could look like this:
Notice how we appended =0
to virtual int area() instead of
specifying an implementation for the function. This type of function is called
a pure virtual function, and all classes that contain at least one pure
virtual function are abstract base classes.
Difference between abstract base class and regular polymorphic class
The main difference between an abstract base class and a regular polymorphic class is that, in abstract base classes at least one of its members lacks implementation and we cannot create instances (objects) of it. But a class that cannot instantiate objects is not totally useless. We can create pointers to it and take advantage of all its polymorphic abilities. Therefore the declaration,
CPolygon poly;
would not be valid
for an abstract base class, because it tries to instantiate an object.
Nevertheless, the following pointers would be perfectly valid:
CPolygon *
ppoly1;
Hence for an abstract class, pointers to the abstract base class are used to point to objects of its derived classes and thereon, to implement its polymorphic functions.
PROGRAM 74
#include <iostream>
using namespace std;
class
CPolygon
{
protected:
int width, height;
public:
void
set_values (int a, int b)
{ width=a; height=b; }
virtual
int area (void) =0;
};
class
CRectangle: public CPolygon
{
public:
int
area (void)
{ return (width * height); }
};
class
CTriangle: public CPolygon
{
public:
int
area (void)
{ return (width * height / 2); }
};
main ()
{
CRectangle rect;
CTriangle trng;
CPolygon * ppoly1 =
▭
CPolygon * ppoly2 =
&trng;
ppoly1->set_values
(4,5);
ppoly2->set_values
(4,5);
cout <<
ppoly1->area() << endl;
cout <<
ppoly2->area() << endl;
}
OUTPUT
20
10
|
Q. Write a program to create a class called
pwr that computes the result of a number
raised to some power. Also print memory size of the declared Object using “this”
pointer.
#include <iostream> using namespace std; class pwr { double
b; int
e ; double
val ; public : pwr(double
base, int exp); double
get_pwr() { cout<<"\nSize of this Object is :
"<<sizeof(this)<<" Bytes\n"; return
val; } }; pwr :: pwr(double base, int exp) { b =
base; e =
exp ; val
= 1; if
(exp==0) return; for(;
exp>0; exp--) val
= val * b; } main() { pwr x(2, 3), y(2.5, 1), z(2.5, 0); cout<< "2 raised to 3 = " <<
x.get_pwr()<<"\n"; cout<< "2.5 raised to 1 = "
<< y.get_pwr()<<"\n"; cout<< "2.5 raised to 0 = "
<< z.get_pwr()<<" \n"; }
|
OUTPUT
Within a
member function, the members of a class can be accessed directly, without any
object or class qualification. Thus, inside pwr(),
the statement
b = base;
means that
the copy of b associated with the invoking object will be assigned the value
contained in base. However, the same statement can also be written like this:
this -> b = base;
The this pointer points to the object that
invoked pwr(). Thus, this -> b refers to
that object’s copy of b. Below is the entire pwr() constructor re-written using
the this pointer:
pwr :: pwr (double base,
int exp)
{
this -> b = base;
this -> e = exp;
this -> val = 1;
if (exp==0) return;
for( ; exp>0; exp--)
(this -> val) = (this ->
val) * (this -> b);
}
Actually, no C++ programmer would write pwr() as just shown above,
because nothing is gained, and the standard form is easier. However, the this pointer is very important, when
operators are overloaded and whenever a member function must utilize a pointer
to the object that invoked it. Remember that this pointer is automatically passed onto all member functions.
Therefore, get_pwr() can also be
rewritten as shown here:
double get_pwr()
{ return this -> val; }
If get_pwr() is
invoked like this:
y.get_pwr() ;
Then this pointer will point to object y.
Note
: Two important points about this
pointer 1. friend functions are not members of any Class, and so they have no this pointer. 2. static member functions do not have a this pointer. |
No comments:
Post a Comment