Polymorphism
Polymorphism is an opportunity for different classes of objects, related through inheritance, to respond in various ways when calling the same function element. It helps to create a universal mechanism describing the behavior of not only the base class, but also descendant classes.
Let's continue to develop a base class CShape, and define a member function GetArea(), designed to calculate the area of a shape. In all the descendant classes, produced by inheritance from the base class, we redefine this function in accordance with rules of calculating the area of a particular shape.
For a square (class CSquare), the area is calculated through its sides, for a circle (class CCircle), area is expressed through its radius etc. We can create an array to store objects of CShape type, in which both objects of a base class and those of all descendant classes can be stored. Further we can call the same function for each element of the array.
Example:
//--- Base class
class CShape
{
protected:
int m_type; // Shape type
int m_xpos; // X - coordinate of the base point
int m_ypos; // Y - coordinate of the base point
public:
void CShape(){m_type=0;}; // constructor, type=0
int GetType(){return(m_type);};// returns type of the shape
virtual
double GetArea(){return (0); }// returns area of the shape
}; |
Now, all of the derived classes have a member function getArea(), which returns a zero value. The implementation of this function in each descendant will vary.
//--- The derived class Circle
class CCircle : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
double m_radius; // circle radius
public:
void CCircle(){m_type=1;}; // constructor, type=1
void SetRadius(double r){m_radius=r;};
virtual double GetArea(){return (3.14*m_radius*m_radius);}// circle area
}; |
For the class Square the declaration is the same:
//--- The derived class Square
class CSquare : public CShape // After a colon we define the base class
{ // from which inheritance is made
private:
double m_square_side; // square side
public:
void CSquare(){m_type=2;}; // constructor, type=1
void SetSide(double s){m_square_side=s;};
virtual double GetArea(){return (m_square_side*m_square_side);}// square area
}; |
For calculating the area of the square and circle, we need the corresponding values of m_radius and m_square_side, so we have added the functions SetRadius() and SetSide() in the declaration of the corresponding class.
It is assumed that object of different types (CCircle and CSquare) derived from one base type CShape are used in our program. Polymorphism allows creating an array of objects of the base CShape class, but when declaring this array, these objects are yet unknown and their type is undefined.
The decision on what type of object will be contained in each element of the array will be taken directly during program execution. This involves the dynamic creation of objects of the appropriate classes, and hence the necessity to use object pointers instead of objects.
The new operator is used for dynamic creation of objects. Each such object must be individually and explicitly deleted using the delete operator. Therefore we will declare an array of pointers of CShape type, and create an object of a proper type for each element (new Class_Name), as shown in the following script example:
//+------------------------------------------------------------------+
//| Script program start function |
//+------------------------------------------------------------------+
void OnStart()
{
//--- Declare an array of object pointers of the base type
CShape *shapes[5]; // An array of pointers to CShape object
//--- Here fill in the array with derived objects
//--- Declare a pointer to the object of CCircle type
CCircle *circle=new CCircle();
//--- Set object properties at the circle pointer
circle.SetRadius(2.5);
//--- Place the pointer value in shapes[0]
shapes[0]=circle;
//--- Create another CCircle object and write down its pointer in shapes[1]
circle=new CCircle();
shapes[1]=circle;
circle.SetRadius(5);
//--- Here we intentionally "forget" to set a value for shapes[2]
//circle=new CCircle();
//circle.SetRadius(10);
//shapes[2]=circle;
//--- Set NULL for the element that is not used
shapes[2]=NULL;
//--- Create a CSquare object and write down its pointer to shapes[3]
CSquare *square=new CSquare();
square.SetSide(5);
shapes[3]=square;
//--- Create a CSquare object and write down its pointer to shapes[4]
square=new CSquare();
square.SetSide(10);
shapes[4]=square;
//--- We have an array of pointers, get its size
int total=ArraySize(shapes);
//--- Pass in a loop through all pointers in the array
for(int i=0; i<5;i++)
{
//--- If the pointer at the specified index is valid
if(CheckPointer(shapes[i])!=POINTER_INVALID)
{
//--- Log the type and square of the shape
PrintFormat("The object of type %d has the square %G",
shapes[i].GetType(),
shapes[i].GetArea());
}
//--- If the pointer has type POINTER_INVALID
else
{
//--- Notify of an error
PrintFormat("Object shapes[%d] has not been initialized! Its pointer is %s",
i,EnumToString(CheckPointer(shapes[i])));
}
}
//--- We must delete all created dynamic objects
for(int i=0;i<total;i++)
{
//--- We can delete only the objects with pointers of POINTER_DYNAMIC type
if(CheckPointer(shapes[i])==POINTER_DYNAMIC)
{
//--- Notify of deletion
PrintFormat("Deleting shapes[%d]",i);
//--- Delete an object by its pointer
delete shapes[i];
}
}
} |
Please note that when deleting an object using the delete operator, the type of its pointer must be checked. Only objects with the POINTER_DYNAMIC pointer can be deleted using delete. For pointers of other type, an error will be returned.
But besides the redefining of functions during inheritance, polymorphism also includes the implementation of one and the same functions with different sets of parameters within a class. This means that the class may have several functions with the same name but with a different type and/or set of parameters. In this case, polymorphism is implemented through the function overload.