Polymorphism

Exercise 1: Polymorphic ToString() Function
The ToString() functions of Point and Line override the ToString() from the Shape base class. We saw that we could put aPoint in a Shape* variable. But when calling the ToString() method on the Shape* variable, the function in Shape was called instead the one in Point. To make the compiler generate the required code to find out what type of object the Shape*variable is actually pointing to so it can call the right version; we need to declare the function as virtual. Thus declare the ToString() function in the Shape class as virtual and test the program again. Is the ToString() function of Point called when you use a Shape* that contains a Point now?

Exercise 2: Calling Base Class Functionality
The ToString() function of the Shape class is overridden in the derived classes. But for the derived class it is still possible to use the base class functionality. In the ToString() function of Point and Line we also want to incorporate the ID from theShape base class.
•In the ToString() method of Point, call the ToString() method of the Shape base class:std::string s=Shape::ToString();
•Append the shape description string to the point description string before returning.
•Do this also for the ToString() function in the Line class (and Circle class).
•Test the application again. Is now the ID printed when printing a point or line?

Exercise 3: Virtual Destructors
When objects are removed from memory, the destructor is called. When a derived class destructor is called, it will automatically call the base class destructor. But when you have pointers to a base class, deleting objects might not be done correctly. If not done already, print some text in the destructors of the Shape, Point and Line classes. Then test the following code:
Shape* shapes[3];
shapes[0]=new Shape;
shapes[1]=new Point;
shapes[2]=new Line;
for (int i=0; i!=3; i++) delete shapes[i];
Will the proper destructors (including the destructor of the Shape base class) be called? In this case, the derived class destructor will only be called when the destructor is declared virtual in the base class. Do this in the Shape class and run the code again. Are the proper destructors called now?

Exercise 4: Abstract Functions
Sometimes functions in the base class are only there to be overridden in the derived class. Assume that you want to draw all the shapes using the following code:
Shape* shapes[10];
shapes[0]=new Line;
shapes[1]=new Point;

shapes[9]=new Line(Point(1.0, 2.5), Point(3.4, 5.2));
for (int i=0; i!=10; i++) shapes[i]->Draw();
for (int i=0; i!=10; i++) delete shapes[i];
Create the Draw() function in the Shape base class and override it in the derived classes (point, line and if present the circle class). Simulate drawing by just printing some text. What implementation did you give the Draw() function in Shape? Shape is just an abstraction to work with various kinds of shapes like lines and circles. Shapes don’t have a physical appearance. Therefore its Draw() function will have an empty implementation. But better is to give it no implementation at all by making it a pure virtual member function:
virtual void Draw()=0;
Do this in your code. Try to create an instance of the Shape class. Is this possible? Now the Shape class is really an abstraction. You don’t make shape instances but you can still create shape pointers that point to concrete shapes like point and line. The Shape class is now an abstract base class.

Exercise 5: Template Method Pattern
In this exercise we are going to create a Print() function that is printing the shape information to the cout object. ThePrint() function can use the ToString() to obtain the string to print. You see that the implementation of Print() is in each derived class largely the same; calling ToString() and sending the result to cout. Since the ToString() function is polymorphic, we can use the polymorphic behavior in the Print() function of Shape. Thus:
•Add a Print() function to the Shape class.
•In this function, call the ToString() function and send the result to the cout object.
•In the test program, create a point and line object and call the Print() function. Does itprint the right information even when point and line do not have the Print() function?
You have now created a function for the base class (Print()) that does all the functionality common to all derived classes. Only the part of that function that is different for each derived class is delegated to a polymorphic function (ToString()). This mechanism is an often used design pattern called the “Template Method Pattern”.

Leave a Reply

Your email address will not be published. Required fields are marked *