Section 4.4 Polymorphism | ||
For a suitable understanding of this section you should clearly know how to use
pointers and inheritance between classes. I recommend that if some
of these expressions seem strange to you, you review the indicated sections:
int a::b(c) {}; // Classes (Section 4.1) a->b // pointers and objects (Section 4.2) class a: public b; // Relationships between classes (Section 4.3) |
// pointers to base class #include <iostream.h> 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 (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << rect.area() << endl; cout << trgl.area() << endl; return 0; } |
20 10 |
The function main creates two pointers that point to objects of class CPolygon, that are *ppoly1 and *ppoly2. These are assigned to the addresses of rect and trgl, and because they are objects of classes derived from CPolygon they are valid assignations.
The only limitation of using *ppoly1 and *ppoly2 instead of rect and trgl is that both *ppoly1 and *ppoly2 are of type CPolygon* and therefore we can only refer to the members that CRectangle and CTriangle inherit from CPolygon. For that reason when calling the area() members we have not been able to use the pointers *ppoly1 and *ppoly2.
To make it possible for the pointers to class CPolygon to admit area() as a valid member, this should also have been declared in the base class and not only in its derived ones. (see the following section).
Take a look at the following example:
// virtual members #include <iostream.h> class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) { return (0); } }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon poly; 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; return 0; } |
20 10 0 |
Now the three classes (CPolygon, CRectangle and CTriangle) have the same members: width, height, set_values() and area().
area() has been defined as virtual because it is later redefined in derived classes. You can verify if you want that if you remove this word (virtual) from the code and then you execute the program the result will be 0 for the three polygons instead of 20,10,0. That is because instead of calling the corresponding area() function for each object (CRectangle::area(), CTriangle::area() and CPolygon::area(), respectively), CPolygon::area() will be called for all of them since the calls are via a pointer to CPolygon.
Therefore what the word virtual does is to allow that a member of a derived class with the same name as one in the base class be suitably called when a pointer to it is used, as in the above example.
Note that in spite of its virtuality we have also been able to declare an object of type CPolygon and to call its area() function, that always returns 0 as the result.
The class CPolygon could have been thus:
Notice how we have appended =0 to virtual int area (void) instead of specifying an implementation for the function. This type of function is called a pure virtual function, and all classes that contain a pure virtual function are considered abstract base classes.// abstract class CPolygon class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; };
The greatest difference of an abstract base class is that instances (objects) of it cannot be created, but we can create pointers to them. Therefore a declaration like:
CPolygon poly;would be incorrect for the abstract base class declared above. Nevertheless the pointers:
CPolygon * ppoly1;are be perfectly valid. This is because the pure virtual function that it includes is not defined and it is impossible to create an object if it does not have all its members defined. Nevertheless a pointer that points to an object of a derived class where this function has been defined is perfectly valid.
CPolygon * ppoly2
Here you have the complete example:
// virtual members #include <iostream.h> 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); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; return 0; } |
20 10 |
// virtual members #include <iostream.h> class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; void printarea (void) { cout << this->area() << endl; } }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = &trgl; ppoly1->set_values (4,5); ppoly2->set_values (4,5); ppoly1->printarea(); ppoly2->printarea(); return 0; } |
20 10 |
Remember that this represents a pointer to the object whose code is being executed.
Abstract classes and virtual members grant to C++ the polymorphic characteristics that make object-oriented programming such a useful instrument. Of course we have seen the simplest way to use these features, but imagine these features applied to arrays of objects or objects assigned through dynamic memory.
© The C++ Resources Network, 2000-2003 - All rights reserved |
Previous: 4-3. Relationships between classes. |
index |
Next: 5-1. Templates. |