CSE230 - C++ Notes 6 Polymorphism and Virtual Functions Polymorphism •It is possible to design and implement systems that are more easily ______________. •Programs can be written to generically process – as base-class objects – objects of all existing classes in a hierarchy. •Classes that do not exist during program development can be added with little or no modification to the generic part of the program – as long as those classes are part of the hierarchy that is being processed generically. •The only part of the program that will need modification are those parts that require direct knowledge of the particular class that is added to the hierarchy. Polymorphism (cont’d) •One means of dealing with objects of different types is to use a _________ statement. •For example, in a hierarchy of shapes in which each shape specifies its type as a data member, a _________ structure could determine which print function to call based on the type of the particular object. •Polymorphism and _________ functions can eliminate the need for switch logic. It also avoids errors typically associated with equivalent switch logic and facilitates testing, debugging and program maintenance. •Suppose a set of shape classes such as Circle, Triangle, Square, etc. are all derived from base class Shape and each class includes a separate draw function. Virtual Functions •To draw any shape, we could simply call function draw of base class Shape and let the program determine ____________ which derived class draw function to use. •To enable this kind behavior, draw must be declared in the base class (i.e. Shape) as a virtual function and then each derived class overrides draw to draw the appropriate shape. •Following may appear in Shape: virtual void draw() const; •Once a function is declared virtual, it remains virtual all the way down the inheritance hierarchy from that point even if it is not declared ________ when a class overrides it. However, explicit declaration promotes clarity. 1 Abstract and Concrete Classes •A class is made abstract by declaring one or more of its _________ functions to be _______. •No objects of an abstract base class can be instantiated. •A ______ virtual function is one with an initializer of =NULL (or =0) in its declaration as in: virtual void draw() const = NULL; // =0 •If no definition is supplied in a derived class for a pure virtual function, then the function remains to be pure. Consequently, the derived class is also an abstract class. #ifndef SHAPE_H #define SHAPE_H // abstract definition for Shape class Shape {public: // impure virtual functions virtual double area() const { return 0.0; } virtual double volume() const { return 0.0; } // pure virtual functions overridden in each of the derived classes virtual void printShapeName() const = _________; // = 0 virtual void print() const = ________; // = 0 // non–virtual functions/data may be defined, but they are not needed in this class }; // end class Shape #endif #ifndef POINT_H #define POINT_H #include <iostream> using std::cout; #include "shape.h" class Point : public Shape { public: Point( int = 0, int = 0 ); // default constructor void setPoint( int, int ); int getX() const { return x; } int getY() const { return y; } virtual void printShapeName() const { cout << "Point: "; } virtual void print() const; // __________ and __________ of a point are both 0.0 and they are inherited from Shape private: int x, y; // x and y coordinates of Point }; // end class Point #endif #include "point.h“ Point::Point( int a, int b ) { setPoint( a, b ); } void Point::setPoint( int a, int b ) { x = a; y = b; } // end function setPoint void Point::print() const { cout << '[' << x << ", " << y << ']'; } 2 #ifndef CIRCLE_H #define CIRCLE_H #include "point.h" class Circle : public _________ { public: // default constructor Circle( double r = 0.0, int x = 0, int y = 0 ); void setRadius( double ); double getRadius() const; ____________ double area() const; virtual void printShapeName() const { cout << "Circle: "; } virtual void print() const; private: double radius; // radius of Circle }; // end class Circle #endif #include <iostream> using std::cout; #include "circle.h" Circle::Circle( double r, int a, int b ) : Point( a, b ) // call base-class constructor { setRadius( r ); } void Circle::setRadius( double r ) { radius = r > 0 ? r : 0; } double Circle::getRadius() const { return radius; } double Circle::area() const { return 3.14159 * radius * radius; } void Circle::print() const { Point::print(); cout << "; Radius = " << radius; } // end function print #ifndef CYLINDER_H #define CYLINDER_H #include "circle.h" class Cylinder : public ___________ { public: // default constructor Cylinder( double h = 0.0, double r = 0.0, int x = 0, int y = 0 ); void setHeight( double ); double getHeight(); virtual double area() const; ______________ double volume() const; virtual void printShapeName() const {cout << "Cylinder: ";} virtual void print() const; private: double height; // height of Cylinder }; // end class Cylinder #endif #include <iostream> using std::cout; #include "cylinder.h" Cylinder::Cylinder( double h, double r, int x, int y ):Circle( r, x, y)//call base-class constructor { setHeight( h ); } double Cylinder::getHeight() { return height; } void Cylinder::setHeight( double h ) { height = h > 0 ? h : 0; } double Cylinder::area() const { // surface area of Cylinder return 2 * Circle::area() + 2 * 3.14159 * getRadius() * height; } // end function area double Cylinder::volume() const { return Circle::area() * height; } 3 void Cylinder::print() const { Circle::print(); cout << "; Height = " << height; } // end function print #include <iostream> #include <iomanip> using namespace std; #include "shape.h" #include "point.h" #include "circle.h" #include "cylinder.h“ // Make virtual function calls off a base-class pointer using dynamic binding. void virtualViaPointer( const __________ *baseClassPtr ) { baseClassPtr->printShapeName(); baseClassPtr->print(); cout<<"\nArea="<<baseClassPtr->area()<<"\nVolume="<< baseClassPtr->volume()<<"\n"; } // end function virtualViaPointer // Make virtual function calls off a base-class reference using dynamic binding. void virtualViaReference( const _________ &baseClassRef ) { baseClassRef.printShapeName(); baseClassRef.print(); cout<<"\nArea="<< baseClassRef.area()<<"\nVolume="<< baseClassRef.volume()<<"\n"; } // end function virtualViaReference int main() { cout << setiosflags( ios::fixed | ios::showpoint ) << setprecision( 2 ); Point point( 7, 11 ); // create a Point Circle circle( 3.5, 22, 8 ); // create a Circle Cylinder cylinder( 10, 3.3, 10, 10 ); // create a Cylinder point.printShapeName(); // ________ binding point.print(); // static binding circle.printShapeName(); // static binding circle.print(); // static binding cylinder.printShapeName(); // static binding cylinder.print(); // ________ binding ___________ *arrayOfShapes[ 3 ]; // array of base-class pointers arrayOfShapes[ 0 ] = &point; // aim arrayOfShapes[0] at derived-class Point object arrayOfShapes[ 1 ] = &circle; // aim arrayOfShapes[1] at derived-class Circle object arrayOfShapes[ 2 ] = &cylinder; // aim arrayOfShapes[2] at derived-class Cylinder object cout << "Virtual function calls made off " << "base-class pointers\n"; for ( int i = 0; i < 3; i++ ) // Loop through arrayOfShapes and call virtualViaPointer virtualViaPointer( arrayOfShapes[ i ] ); cout << "Virtual function calls made off " << "base-class references\n"; for ( int j = 0; j < 3; j++ ) // Loop through arrayOfShapes and call virtualViaReference virtualViaReference( *arrayOfShapes[ j ] ); return 0; } // end function main 4