Objective:
- Concept of Polymorphism
- Use base class pointers to call derived class objects
- Declare virtual functions and destructors
Activity:
Task 1:
- Create a class shape and also create classes circle, square, triangle and rectangle as sub classes of class shape
- Each sub class of shape should override a calculate area of class shape
- Your classes should have overloaded constructors that will take the member variables as input
Task 2:
What if we wanted to use the definition of the derived class function? To accomplish this, we can add the keyword virtual to the declaration of the area() method in the shape class. Specifying a function as virtual makes sure that whenever we use a base class pointer/ref pointing to an object of a derived class to call a function, the definition of the method declared in the derived class is used.
Task 3:
In the above exercises, we have seen a very simple implementation of Polymorphism. The real power of this feature is realized when we have a collection of objects of multiple derived classes and we use a pointer of the base class to call their respective overloaded methods. Or you can pass derived class object to a function with formal parameter of base class type.
Create a function sumArea that takes two shapes of any type and return the sum of their area (shape/rectangle/circle etc...). Note that sumArea is a nonmember function.
Task 4:
Another advantage of polymorphism is to keep the object of different sub types of same parent class in one array. The array will be on parent class pointer type. As an exercise create take 5 shapes as input from user. Store these in one array. In one loop print the area of these shapes.
Task 5:
Although things seem to be fine on the surface, there is a problem in the program we just wrote. To observe this problem, we must add destructors for all classes. Add the definitions of the destructors in their corresponding classes.
Can you see what went wrong? When using delete to deallocate memory, only the base class destructor is called whereas the derived class destructor is not called at all. This can cause a memory leak issue. To avoid this we declare the base class destructor as virtual. Doing this will make sure that the derived class destructor is called even if you are using a base class pointer to call the destructor.
Solution:
#include<iostream>
#include<conio.h>
#include<string>
using namespace std;
class shape
{
public:
string color;
shape(string c)
{
color = c;
}
virtual float area()
{
return 0;
}
virtual ~shape()
{
cout << "~shape() called." << endl;
}
};
class circle : public shape
{
float radius;
public:
circle(float r, string c) : shape(c), radius(r)
{
cout << "Circle constructor invoked." << endl;
}
float area()
{
return 3.14 * radius * radius;
}
~circle()
{
cout << "~circle() called." << endl;
}
};
class square : public shape
{
float side;
public:
square(float s, string c) : shape(c), side(s)
{
cout << "Square constructor invoked." << endl;
}
float area()
{
return side * side;
}
~square()
{
cout << "~square() called." << endl;
}
};
class rectangle : public shape
{
float length, width;
public:
rectangle(float l, float w, string c) : shape(c), length(l), width(w)
{
cout << "Rectangle constructor invoked." << endl;
}
float area()
{
return length * width;
}
};
class triangle :public shape
{
float base, height;
public:
triangle(float b, float h, string c) :shape(c)
{
base = b;
height = h;
cout << "Triangle constructor invoked." << endl;
}
float area()
{
return (base * height) / 2;
}
~triangle()
{
cout << "~triangle() called." << endl;
}
};
int sumArea(shape& s1, shape& s2)
{
return s1.area() + s2.area();
}
int main()
{
triangle t1(1.0, 9.0, "Red");
circle c1(2, "Blue");
rectangle r1(6, 2, "Orange");
cout << t1.area() << endl;
cout << t1.color << endl;
cout << c1.area() << endl;
cout << r1.area() << endl;
shape* sptr1 = &t1;
shape& sref = r1;
cout << sptr1->area() << endl;
cout << sptr1->color << endl;
cout << sref.color << endl;
cout << sref.area() << endl;
shape s1("Purple");
cout << sumArea(t1, c1) << endl;
cout << sumArea(s1, r1) << endl;
cout << sumArea(s1, t1) << endl;
int count = 5;
shape* shapesArray[5];
string color;
for (int i = 0; i < count; i++)
{
cout << "Press 1 for a triangle, 2 for rectangle, and 3 for a circle." << endl;
switch (_getch())
{
case '1':
float base, height;
cout << "Enter base: ";
cin >> base;
cout << "Enter height: ";
cin >> height;
cout << "Enter color: ";
cin >> color;
shapesArray[i] = new triangle(base, height, color);
break;
case '2':
float length, width;
cout << "Enter length: ";
cin >> length;
cout << "Enter width: ";
cin >> width;
cout << "Enter color: ";
cin >> color;
shapesArray[i] = new rectangle(length, width, color);
break;
case '3':
float radius;
cout << "Enter radius: ";
cin >> radius;
cout << "Enter color: ";
cin >> color;
shapesArray[i] = new circle(radius, color);
break;
default:
cout << "Invalid input. Enter again." << endl << endl;
i--;
break;
}
}
cout << endl << "Printing areas of shapes:" << endl;
for (int i = 0; i < count; i++)
{
cout << "Shape " << i + 1 << " has area: " << shapesArray[i]->area() << endl;
}
for (int i = 0; i < count; i++)
{
delete shapesArray[i];
}
return 0;
}