CS415 C++ Programming - Lab #7 CS415 C++ Programming - Lab #7

Objectives:
  1. Understand the concept of polymorphism using virtual function.
  2. Understand the usage of protected keyword.

Notes:

Procedures:
  1. The objective of this assignment is to extend the available types of graphics objects in Lab #6's program.

    In this program, four different graphics objects (one Rectangle, two RectangleFilled, and one Cross) are created and user can move their position and size by commands.

    The graphics objects to be created are Rectangle, RectangleFilled, and Cross.

    You are required to create class Shape, besides class Rectangle, RectangleFilled, and Cross (Rectangle class also needs to be modified). Regarding the output device, use class Canvas and class CharScreen defined in Lab #6. The class hieararchy is shown in Figure 1.

    The purpose to create class Shape is to bring extensibility to the program, similarly to class Canvas.

    Class Rectangle, RectangleFilled, and Cross can be defined by inheriting class Shape and add additional features. If you want define more graphics objects with new geometric shapes, you can take advantage of Shape class in the same way.

    Class Shape contains attributes such as centerX, centerY, sizeX , and sizeY (these are declared as protected so that they can be directly accessed in the derived class also) and member functions such as setSize(), moveTo(), moveDelta() and draw(). All of these members are common features which any types of "Shape" such as Rectangle, RectangleFilled, Cross, etc. should have (moveDelta() has been added to realize "relative" movement. See the execution result for its behavior).

    First, you create class Rectangle and class Cross by inheriting class Shape.

    The ways to draw a Rectangle and Cross should be different because they have a different geometric shape. Therefore, you need to define draw() function for each class which has a different body.

    Next, you create class RectangleFilled by inheriting class Rectangle. In class RectangleFilled, one additional attribute fillCharacter is added. To set this attribute, setFillCharacter() needs to be defined. Also, draw() function needs to be defined for this class. This draw() uses fillCharacter to fill the inside in.

    A RectangleFilled can be drawn by filling the inside of the rectangle first and then overlaying the border. This means, in RectangleFilled::draw(), first you fill the inside of the rectangle with fillCharacter, and then you overlay the border by calling draw() function in class Rectangle. (Use a notation Rectangle::draw(); to call draw() defined in Rectangle. "::" is called scope resolution operator.)

    The key point of this program is to store a pointer of a derived class object into a superclass's pointer variable to introduce polymorphism.

    In function createShapes(), four different graphics objects are created, and the pointers to every objects are stored in array shapes whose elements are pointers to Shape (This is possible in case that Rectangle, RectangleFilled and Cross are all derived from class Shape. If they are not derived class, the compiler will make a compilation error).

    Looking at showShapes(), it is just saying "draw()" for each element of array shapes. However, the "draw()" will behave differently depending on what type of object shapes[i] points to. If shape[i] points to a Rectangle object, shape[i]->draw() will call Rectangle::draw(), and if shape[i] points to a Cross object, shape[i]->draw() will call Cross::draw(), and so on.

    Thus, even if the name of the message is same (like draw), the object that receives the message behaves differently depending on its object type. This concept is called polymorphism. Also, in this program, selection of appropriate functions is done in runtime. This is called runtime binding (or dynamic binding). (Another example of polymorphism with static binding appears in operator overloading which will be covered in the later lecture).

    Note that, to make a member function polymorphic, you need to declare draw() as "virtual" in base class (Shape). If virtual is not added, shape[i]->draw() will just call Shape::draw() which is left as a blank in this program. Other functions without virtual such as setSize() etc. are not polymorphic.

    Also note that "virtual void show() const { }" in Canvas.h has been added and the argument for showShapes() in main.cpp has been changed to be "const Canvas*". This makes the show() function polymorphic. Meaning, when you define a new display device class by inheriting Canvas class, you don't have to modify this callee side code.

    Thus, in this program, polymorphism is happening in both class hierarchies for display devices and for graphics objects.

    A sample execution result of this program is shown in Figure 2.


    Figure 1
    Class Hierarchy

    List 1
    //
    // Canvas.h -- Canvas class
    //
    
    #ifndef CANVAS_H
    #define CANVAS_H
    
    class Canvas {
    private:
        char* buf;  // buffer body
        int sizeX;  // size of X direction
        int sizeY;  // size of Y direction
    
    public:
        Canvas(int sx, int sy);
        ~Canvas(void);
    
        int getSizeX() const;
        int getSizeY() const;
        void putPixel(int x, int y, char pixel) const;
        char getPixel(int x, int y) const;
        void clear() const;
        virtual void show() const { };   // <-- add this
    };
    #endif // CANVAS_H
    
    List 2
    //
    // Canvas.cpp -- Canvas class implementation
    //
    
    // (Same as Lab #6)
    
    List 3
    //
    // CharScreen.h -- CharScreen class 
    //
    //  (inherits Canvas class.
    //  display functionality to physical screen device is added)
    //
    
    // (Same as Lab #6)
    
    List 4
    //
    // CharScreen class implementation
    //
    
    // (Same as Lab #6)
    
    List 5
    //
    // Shape.h -- Shape class
    //
    
    #ifndef SHAPE_H
    #define SHAPE_H
    
    class Canvas;
    
    class Shape {
    protected:  // to make it accessible in derived class
        double centerX, centerY;
        double sizeX, sizeY;
    
    public:
        Shape(void);
        void setSize(double sx, double sy);
        void moveTo(double x, double y);
        void moveDelta(double dx, double dy);
        virtual void draw(const Canvas* s) const { };
    };
    
    #endif
    
    List 6
    //
    // Shape.cpp -- Shape class implementation
    //
    
    #include "Shape.h"
    
    Shape::Shape(void)
      : centerX(0.0), centerY(0.0), sizeX(0.0), sizeY(0.0)
    {
    }
    
    //
    // move to absolute position
    // 
    void Shape::moveTo(double x, double y)
    {
        centerX = x;
        centerY = y;
    }
    
    //
    // move to relative position
    //
    void Shape::moveDelta(double dx, double dy)
    {
        centerX += dx;
        centerY += dy;
    }
    
    //
    // set size
    //
    void Shape::setSize(double sx, double sy)
    {
        sizeX = sx;
        sizeY = sy;
    }
    
    
    List 7
    //
    // Rectangle.h -- Rectangle class
    //
    
    #ifndef RECTANGLE_H
    #define RECTANGLE_H
    
    #include "Shape.h"
    
    class Canvas;
    
    class Rectangle : public Shape {
    private:
    
    public:
        void draw(const Canvas* s) const;
    };
    
    #endif
    
    List 8
    //
    // Rectangle class implementation
    //
    
    #include "Rectangle.h"
    #include "Canvas.h"
    
    void Rectangle::draw(const Canvas* s) const
    {
      // (Same as Lab #6)
    }
    
    
    List 9
    //
    // RectangleFilled.h -- RectangleFilled class
    //
    
    #ifndef RECTANGLE_FILLED_H
    #define RECTANGLE_FILLED_H
    
    #include "Rectangle.h"
    
    class Canvas;
    
    class RectangleFilled : public Rectangle {
    private:
        char fillSymbol;  // additional attribute
    
    public:
        RectangleFilled() : fillSymbol('/') { } // default fillSymbol = '/'
        void setFillSymbol(const char sym);
        void draw(const Canvas* s) const;
    };
    
    #endif
    
    
    List 10
    //
    // RectangleFilled.cpp -- RectangleFilled class implementation
    //
    
    #include "RectangleFilled.h"
    #include "Canvas.h"
    
    void RectangleFilled::draw(const Canvas* s) const
    {
      // Add your code here
    }
    
    void RectangleFilled::setFillSymbol(const char sym)
    {
      // Add your code here
    }
    
    
    List 11
    //
    // Cross.h -- Cross class
    //
    
    #ifndef CROSS_H
    #define CROSS_H
    
    #include "Shape.h"
    
    class Canvas;
    
    class Cross : public Shape {
    private:
    public:
        void draw(const Canvas* s) const;
    };
    
    #endif
    
    List 12
    //
    // Cross.cpp -- Cross class implementation
    //
    #include "Cross.h"
    #include "Canvas.h"
    
    void Cross::draw(const Canvas* s) const
    {
      // Add your code here
    }
    
    List 13
    //
    // main.cpp -- A simple graphics object
    //
    
    #include <iostream.h>
    #include <stdlib.h>
    
    #include "CharScreen.h"
    #include "Rectangle.h"
    #include "RectangleFilled.h"
    #include "Cross.h"
    
    const int scrSizeX = 70;         // screen size X
    const int scrSizeY = 20;         // screen size Y
    const double defaultSizeX = 5.0; // default size X
    const double defaultSizeY = 5.0; // default size Y
    
    const int numShapes = 4;         // number of shapes
    
    Shape* shapes[numShapes];        // array of pointers to Shape
    
    void createShapes(void)
    {
        int i = 0;
        Rectangle* r;
        RectangleFilled* rf;
        Cross* c;
        
        r = new Rectangle();
        r->setSize(defaultSizeX, defaultSizeY);
        r->moveTo(15.0, 5.0);
        shapes[i++] = r;
    
        rf = new RectangleFilled();
        rf->setSize(defaultSizeX, defaultSizeY);
        rf->moveTo(35.0, 5.0);
        shapes[i++] = rf;
    
        rf = new RectangleFilled();
        rf->setFillSymbol('=');
        rf->setSize(defaultSizeX, defaultSizeY);
        rf->moveTo(15.0, 13.0);
        shapes[i++] = rf;
    
        c = new Cross();
        c->setSize(defaultSizeX, defaultSizeY);
        c->moveTo(35.0, 13.0);
        shapes[i++] = c;
    }
    
    //
    // Show all shapes
    //
    void showShapes(const Canvas* s)  // <-- different from Lab #6
    {
        s->clear();
        for (int i = 0; i < numShapes; i++) {
            shapes[i]->draw(s);
        }
        s->show();
    }
    
    //
    // Move all shapes
    //
    void moveShapes(double dx, double dy)
    {
        for (int i = 0; i < numShapes; i++) {
            shapes[i]->moveDelta(dx, dy);
        }
    }
    
    //
    // Change the size of all shapes
    //
    void setShapeSize(double sx, double sy)
    {
        for (int i = 0; i < numShapes; i++) {
            shapes[i]->setSize(sx, sy);
        }
    }
    
    int main()
    {
        char c;
        double x, y;
        CharScreen s(scrSizeX, scrSizeY);
    
        createShapes();
    
        for (;;) { // loop forever
            showShapes(&s);
    	
            cout << "Command: {(m)oveDelta, (s)etSize} <x> <y> ";
            cin >> c >> x >> y;
    
            switch (c) {
            case 'm':
                moveShapes(x, y);
    	    break;
    	case 's':
                setShapeSize(x, y);
    	    break;
    	default:
    	    return 0;
    	    break;
    	}
        }
    }
    
    
    List 14
    #
    # Makefile
    #
    main : Canvas.o CharScreen.o Shape.o Rectangle.o RectangleFilled.o Cross.o main.o
    	g++ -o main Canvas.o CharScreen.o Shape.o Rectangle.o RectangleFilled.o Cross.o main.o 
    
    Canvas.o : Canvas.h Canvas.cpp
    	g++ -c Canvas.cpp
    
    CharScreen.o : CharScreen.h CharScreen.cpp Canvas.h Canvas.cpp
    	g++ -c CharScreen.cpp
    
    Shape.o : Shape.h Shape.cpp CharScreen.h CharScreen.cpp
    	g++ -c Shape.cpp
    
    Rectangle.o : Shape.h Shape.cpp Rectangle.h Rectangle.cpp CharScreen.h CharScreen.cpp
    	g++ -c Rectangle.cpp
    
    RectangleFilled.o : Shape.h Shape.cpp RectangleFilled.h RectangleFilled.cpp CharScreen.h CharScreen.cpp
    	g++ -c RectangleFilled.cpp
    
    Cross.o : Shape.h Shape.cpp Cross.h Cross.cpp CharScreen.h CharScreen.cpp
    	g++ -c Cross.cpp
    
    main.o : Canvas.h Canvas.cpp CharScreen.h CharScreen.cpp Shape.h Shape.cpp Rectangle.h Rectangle.cpp RectangleFilled.h RectangleFilled.cpp Cross.h Cross.cpp main.cpp
    	g++ -c main.cpp
    
    clean :
    	rm main *.o *~
    
    Figure 1
    naur[11] main
                                                                          
                                                                          
                                                                          
                 *****               *****                                
                 *   *               *///*                                
                 *   *               *///*                                
                 *   *               *///*                                
                 *****               *****                                
                                                                          
                                                                          
                                                                          
                 *****                 *                                  
                 *===*                 *                                  
                 *===*               *****                                
                 *===*                 *                                  
                 *****                 *                                  
                                                                          
                                                                          
                                                                          
                                                                          
    Command: {(m)oveDelta, (s)etSize} <x> <y> m 3 3
                                                                          
                                                                          
                                                                          
                                                                          
                                                                          
                                                                          
                    *****               *****                             
                    *   *               *///*                             
                    *   *               *///*                             
                    *   *               *///*                             
                    *****               *****                             
                                                                          
                                                                          
                                                                          
                    *****                 *                               
                    *===*                 *                               
                    *===*               *****                             
                    *===*                 *                               
                    *****                 *                               
                                                                          
    Command: {(m)oveDelta, (s)etSize} <x> <y> m -2 -3
                                                                          
                                                                          
                                                                          
                  *****               *****                               
                  *   *               *///*                               
                  *   *               *///*                               
                  *   *               *///*                               
                  *****               *****                               
                                                                          
                                                                          
                                                                          
                  *****                 *                                 
                  *===*                 *                                 
                  *===*               *****                               
                  *===*                 *                                 
                  *****                 *                                 
                                                                          
                                                                          
                                                                          
                                                                          
    Command: {(m)oveDelta, (s)etSize} <x> <y> s 7 5
                                                                          
                                                                          
                                                                          
                 *******             *******                              
                 *     *             */////*                              
                 *     *             */////*                              
                 *     *             */////*                              
                 *******             *******                              
                                                                          
                                                                          
                                                                          
                 *******                *                                 
                 *=====*                *                                 
                 *=====*             *******                              
                 *=====*                *                                 
                 *******                *                                 
                                                                          
                                                                          
                                                                          
                                                                          
    Command: {(m)oveDelta, (s)etSize} <x> <y> s 5 3
                                                                          
                                                                          
                                                                          
                                                                          
                  *****               *****                               
                  *   *               *///*                               
                  *****               *****                               
                                                                          
                                                                          
                                                                          
                                                                          
                                                                          
                  *****                 *                                 
                  *===*               *****                               
                  *****                 *                                 
                                                                          
                                                                          
                                                                          
                                                                          
                                                                          
    Command: {(m)oveDelta, (s)etSize} <x> <y> 
    

Copyright (C) Takamitsu Kawai 2001 All Rights Reserved.