Introduction
Polymorphism
Polymorphism is the ability to associate more than one implementation with the same operation. The binding of the operation with the implementation, can be decided at compile time or at run time. In object orientation, polymorphism can exist in two ways.- Hierarchy independent - This takes the form of overloading, where the same name of an operation, has different implementations in different classes and these classes need not be related by class hierarchy.
- Hierarchy dependent- Here, an operation defined in the superclass can have any number of implementations along the class hierarchy. This type of polymorphism is referred to as universal polymorphism or overriding. The operation can be bound to any of the implementations. This resolution can take place, at compile time or at run time.
Pointers
Address and Pointers
The ideas behind pointers are not complicated. Here’s the first key concept: Every byte in the computer’s memory has an address. Addresses are numbers, just as they are for houses on a street. The numbers start at 0 and go up from there—1, 2, 3, and so on. If you have 1MB of memory, the highest address is 1,048,575. (Of course you have much more.)Your program, when it is loaded into memory, occupies a certain range of these addresses. That means that every variable and every function in your program starts at a particular address.
Pointers to void
Before we go on to see pointers at work, we should note one peculiarity of pointer data types.Ordinarily, the address that you put in a pointer must be the same type as the pointer. You can’t assign the address of a float variable to a pointer to int, for example:float flovar = 98.6; int* ptrint = &flovar; //ERROR: can’t assign float* to int*
void* ptr; //ptr can point to any data type
The next example uses a pointer to void and also shows that, if you don’t use void, you must be careful to assign pointers an address of the same type as the pointer. Here’s the listing for PTRVOID:
// ptrvoid.cpp // pointers to type void #includeusing namespace std; int main() { int intvar; //integer variable float flovar; //float variable int* ptrint; //define pointer to int float* ptrflo; //define pointer to float void* ptrvoid; //define pointer to void ptrint = &intvar; //ok, int* to int* // ptrint = &flovar; //error, float* to int* // ptrflo = &intvar; //error, int* to float* ptrflo = &flovar; //ok, float* to float* ptrvoid = &intvar; //ok, int* to void* ptrvoid = &flovar; //ok, float* to void* return 0; }
If for some unusual reason you really need to assign one kind of pointer type to another, you can use the reinterpret_cast. For the lines commented out in PTRVOID, that would look like this:
ptrint = reinterpret_cast<int*>(flovar); ptrflo = reinterpret_cast<float*>(intvar);
Base class Pointers
In C++ a pointer of a base class can point to an object of any of its derived class.In the example given below shows what happens when a base class and derived classes all have functions with the same name, and you access these functions using pointers. Here is the listing of BASEPOINT
// basepoint.cpp // normal functions accessed from pointer #includeusing namespace std; //////////////////////////////////////////////////////////////// class Base //base class { public: void show(){ cout << "Base\n"; } }; //////////////////////////////////////////////////////////////// class Derv1 : public Base //derived class 1 { public: void show() { cout << "Derv1\n"; } }; //////////////////////////////////////////////////////////////// class Derv2 : public Base //derived class 2 { public: void show() { cout << "Derv2\n"; } }; //////////////////////////////////////////////////////////////// int main() { Derv1 dv1; //object of derived class 1 Derv2 dv2; //object of derived class 2 Base* ptr; //pointer to base class Derv1 *pdv1; //pointer to Derv1 Derv2 *pdv2; //pointer to Derv2 ptr = &dv1; //put address of dv1 in pointer ptr->show(); //execute show() ptr = &dv2; //put address of dv2 in pointer ptr->show(); //execute show() ptr = &dv1; //put address of dv1 in pointer if(reinterpret_cast<Derv1 *>(ptr) !=0){ pdv1 = reinterpret_cast<Derv1 *>(ptr); pdv1->show(); } ptr = &dv2; //put address of dv2 in pointer if(reinterpret_cast<Derv2 *>(ptr) !=0){ pdv2 = reinterpret_cast<Derv2 *>(ptr); pdv2->show(); } return 0; }
ptr = &dv1; // derived class address in base class pointer
Now the question is, when you execute the line
ptr->show();
ptr->show();
Base
Base
As you can see, the function in the base class is always executed. The compiler ignores the contents of the pointer ptr and chooses the member function that matches the type of the pointer, as shown in Figure below.
Sometimes this is what we want, but it doesn’t solve the problem posed at the beginning of this section: accessing objects of different classes using the same statement.
You can use reinterpret_cast to cast the pointer of it's type and use it. But that doesn't solve the problem if u have hundreds of derived class from the base class.
Virtual Functions
Virtual means existing in appearance but not in reality. When virtual functions are used, a program that appears to be calling a function of one class may in reality be calling a function of a different class. Why are virtual functions needed? Suppose you have a number of objects of different classes but you want to put them all in an array and perform a particular operation on them using the same function call. For example, suppose a geometry program includes several different shapes: a triangle, a circle, a square, and so on. Each of these classes has a member function getArea() that returns the area of the object.Now suppose you plan to calculate the area by grouping a number of these elements together, and you want to calculate in a convenient way. One approach is to create an array that holds pointers to all the different objects. The array might be defined like this.
shape *ptrarr[100]; //array of 100 pointers to shapes
If you insert pointers to all the shapes into this array, you can then calculate area using a simple loop:
for(int j=0; j<N; j++) cout << ptrarr[j]->getArea() << endl;
For the polymorphic approach to work, several conditions must be met.
- First, all the different classes of shapes, such as circle and triangles, must be descended from a single base class (called shape in Geometry).
- Second, the getArea() function must be declared to be virtual in the base class.
Virtual Member Functions Accessed with Pointers
Let’s make a single change in our program (BASEPOINT): We’ll place the keyword virtual in front of the declarator for the show() function in the base class. Here’s the listing for the resulting program,VIRTPOINT:// virtpoint.cpp // virtual functions accessed from pointer #includeusing namespace std; //////////////////////////////////////////////////////////////// class Base //base class { public: virtual void show(){ cout << "Base\n"; } }; //////////////////////////////////////////////////////////////// class Derv1 : public Base //derived class 1 { public: void show() { cout << "Derv1\n"; } }; //////////////////////////////////////////////////////////////// class Derv2 : public Base //derived class 2 { public: void show() { cout << "Derv2\n"; } }; //////////////////////////////////////////////////////////////// int main() { Derv1 dv1; //object of derived class 1 Derv2 dv2; //object of derived class 2 Derv1 *pdv1; Derv2 *pdv2; Base* ptr; //pointer to base class ptr = &dv1; //put address of dv1 in pointer ptr->show(); //execute show() ptr = &dv2; //put address of dv2 in pointer ptr->show(); //execute show() ptr = &dv1; //put address of dv1 in pointer if(dynamic_cast<Derv1 *>(ptr) !=0){ pdv1 = reinterpret_cast<Derv1 *>(ptr); pdv1->show(); } ptr = &dv2; //put address of dv2 in pointer if(dynamic_cast<Derv2 *>(ptr) !=0){ pdv2 = reinterpret_cast<Derv2 *>(ptr); pdv2->show(); } system("PAUSE"); return 0; }
- Derv1
- Derv2
- Derv1
- Derv2
ptr->show();
You can use dynamic_cast operator to cast to a particular derived type for the polymorphic classes.
Late Binding
The astute reader may wonder how the compiler knows what function to compile. In BASEPOINT the compiler has no problem with the expressionptr->show();
We’ll put these ideas to use in a moment, but first let’s consider a refinement to the idea of virtual functions.
Abstract Classes and Pure Virtual Functions
Think of the shape class. We’ll never make an object of the shape class; we’ll only make specific shapes such as circles and triangles. When we will never want to instantiate objects of a base class, we call it an abstract class. Such a class exists only to act as a parent of derived classes that will be used to instantiate objects. It may also provide an interface for the class hierarchy.How can we make it clear to someone using our family of classes that we don’t want anyone to instantiate objects of the base class? We could just say this in the documentation, and count on the users of the class to remember it, but of course it’s much better to write our classes so that such instantiation is impossible. How can we can do that? By placing at least one pure virtual function in the base class. A pure virtual function is one with the expression =0 added to the declaration. This is shown in the VIRTPURE example.
// virtpure.cpp // pure virtual function #includeusing namespace std; //////////////////////////////////////////////////////////////// class Base //base class { public: virtual void show() = 0; //pure virtual function }; //////////////////////////////////////////////////////////////// class Derv1 : public Base //derived class 1 { public: void show() { cout << “Derv1\n”; } }; //////////////////////////////////////////////////////////////// class Derv2 : public Base //derived class 2 { public: void show() { cout << “Derv2\n”; } }; //////////////////////////////////////////////////////////////// int main() { // Base bad; //can’t make object from abstract class Base* arr[2]; //array of pointers to base class Derv1 dv1; //object of derived class 1 Derv2 dv2; //object of derived class 2 arr[0] = &dv1; //put address of dv1 in array arr[1] = &dv2; //put address of dv2 in array arr[0]->show(); //execute show() in both objects arr[1]->show(); return 0; }
virtual void show() = 0; // pure virtual function
The equal sign here has nothing to do with assignment; the value 0 is not assigned to anything. The =0 syntax is simply how we tell the compiler that a virtual function will be pure. Now if in main() you attempt to create objects of class Base, the compiler will complain that you’re trying to instantiate an object of an abstract class. It will also tell you the name of the pure virtual function that makes it an abstract class. Notice that, although this is only a declaration, you never need to write a definition of the base class show(), although you can if you need to.
Once you’ve placed a pure virtual function in the base class, you must override it in all the derived classes from which you want to instantiate objects. If a class doesn’t override the pure virtual function, it becomes an abstract class itself, and you can’t instantiate objects from it (although you might from classes derived from it). For consistency, you may want to make all the virtual functions in the base class pure. As you can see, we’ve made another, unrelated, change in VIRTPURE: The addresses of the member functions are stored in an array of pointers and accessed using array elements. This works in just the same way as using a single pointer. The output of VIRTPURE is the same as
VIRT:
- Derv1
- Derv2
Virtual Destructors
Base class destructors should always be virtual. Suppose you use delete with a base class pointer to a derived class object to destroy the derived-class object. If the base-class destructor is not virtual then delete, like a normal member function, calls the destructor for the base class, not the destructor for the derived class.Points to remember
- The process of resolving a function call to an object is called binding
- Associating an object to a function call at run-time is referred to as late binding.
- A pointer of a base class can point to an object of any of its derived class.
- The virtual keyword is used to declare a function as virtual.
- Virtual functions that have no body are called pure virtual functions.
- A class containing one or more pure virtual functions is called an abstract class.
- Instances of an abstract class cannot be created but pointers to it can created.
- Static polymorphism involves binding of a function on the basis of the number, type and sequence of its arguments.
- Function overloading is an example of static polymorphism because functions can be bound to the calls at compile time.
- A function exhibits dynamic polymorphism when it exists in more than one form, and calls to its various forms are resolved dynamically when the program executed.
- Static binding is considered to be more efficient while dynamic binding is more flexible.
Steps to attack the problem sets
- Step 1: Read the Introduction and try sample programs given in the introduction(You can copy paste the programs)
- Step 2: Go through the given PPT on Polymorphism and try sample problems given in the PPT.(you can copy paste the programs from the PPT)
- Step 3: Go through the additional resources give for further clarification.
- Step 4: Attack the problem sets in the given order(Problem Set A and then Problem Set B).
- Step 5: If you are not clear with the problem sets then contact your respective mentor for clarification.
Don't get confused with the example given in PPT and in the introduction part of this Module for shapes. In PPT, Shapes are used with respect to point in a plane and in introduction part shapes is used with respect to Geometry(area, sides, circumference etc).
Resources
PPT:- Object-Oriented Programming: Polymorphism -- 'How to C++ by Ditel and Associates'
Problem Sets
Problem Set A
1. Create a set of classes of Shapes (triangle, quadrilateral, circle) with the following inheritance relationship.Declare and define the classes as
- Shapes-- Abstract base class.
- Triangle -- Abstract derived class with base class as Shapes
- IsoscelesTriangle -- derived class with base class as Triangle.(Attribute with two sides)
- EquilateralTriangle -- derived class with base class as Triangle(Attribute with one side)
- Quadrilateral --Abstract derived class with base class as Shapes.
- Rectangle -- derived class with base class as Quadrilateral.(Attribute with two sides-- length and breadth)
- Square -- derived class with base class as Quadrilateral.(Attribute with one side)
- Rhombus -- derived class with base class as Quadrilateral. (Attribute with two sides-- length and breadth)
- Circle -- Derived class with base class as Shapes. (Attribute with radius)
- Triangle -- Abstract derived class with base class as Shapes
#includeusing namespace std; //////////////////////////////////////////////////////////////// class Shapes //Abstract base class { public: virtual double getArea()=0; //Calculates the Area and returns the area virtual double getPerimeter()=0; //Calculates the perimeter and return the perimeter. virtual void getData()=0; //Take input from the user and store in the state of the object. virtual ~Shapes(){}; }; class Triangle : public Shapes //Derived Abstract class { //Write your code here }; class Quadrilateral : public Shapes //Derived Abstract class { public: //Write your code here }; //Write your code here to declare and define other classes stated int main(){ //Write your code here to declare an array pointer of Shapes[6]. //Write a Loop for the Menu to create an instance of the object // andget the input from the user for the selected Shape //and add the pointer to the Shapes array. //Write a Loop to Print the Area and Perimeter of the array shapes. system("PAUSE"); }
- Create an Instance of
- Triangle
- Create IsoscelesTriangle
- Create EquilateralTriangle
- Quadrilateral
- Create Rectangle
- Create Square
- Create Rhombus
- Create Circle
- Triangle
Create should create an instance of the derived class selected and then call the getData() function of that class and store that instance in the array of Shapes(Base Class).
You can add constructors for the derived classes with default values.
Write a test main function to take the input from the user for all the shapes and store them in a array, then print the area and perimeter of the class.
Refer: http://www.math.com/tables/geometry/index.htm
All the measurements is in Centimeters
Comments
Post a Comment