Shapes - a dynamic binding example
The Inheritance Heirarchy
Operations:
- Draw the shape
- Identify the object being drawn
- Report that an error ocurred
The Shape Class
class Shape
{
public:
virtual void Draw ( ) const = 0; // pure virtual method
virtual void Error ( ) const; // virtual method
void ObjectID ( ) const; // non-virtual method
};
void Shape::Error ( ) const
{
cerr << "Shape error" << endl;
}
void Shape::ObjectID ( ) const
{
cout << "A shape" << endl;
}
Draw ( )
- Draw( ) is a pure virtual member function (hence the " = 0 ")
- Only the interface (not the implementation) of a pure virtual
function is inherited by derived classes.
- Derived classes are expected to have their own implementations
for the virtual method.
- A class with one or more pure virtual methods cannot be instantiated.
- A class that cannot be instantiated is called an abstract class
- A class that can be instantiated is called a concrete class.
Error ( )
- Error( ) is a virtual (not pure virtual) member function.
- virtual functions provide both an interface and an implementation
to derived classes.
- The derived classes may override the inherited
implementation if they wish.
- If the implementation is not overridden, the default behavior of the
base class will be used.
ObjectId( )
- ObjectID( ) is a "regular", non-virtual member function.
- Non-virtual member functions are provided in a base class so that derived classes
inherit a member function's interface as well as a "mandatory" implementation.
- A non-virtual member function specifies behavior that is not supposed to change.
That is, it is not supposed to be overridden.
The Circle Class
class Circle : public Shape
{
public:
virtual void Draw( ) const; // method for drawing a circle
virtual void Error ( ) const; // overriding Shape::Error( )
};
void Circle::Draw ( ) const
{
// code for drawing a circle
}
void Circle::Error ( ) const
{
cerr << "Circle Error" << endl;
}
The Rectangle Class
class Rectangle : public Shape
{
public:
virtual void Draw( ) const; // method for drawing a rectangle
virtual void Error ( ) const; // overriding Shape::Error( )
};
void Rectangle::Draw ( ) const
{
// code for drawing a rectangle
}
void Rectangle::Error ( ) const
{
cerr << "Rectangle Error" << endl;
}
Back to Dynamic Binding
Now, consider these pointers
Shape *pShape; // static type = "pointer to Shape"
Circle *pCircle = new Circle; // static type = "pointer to Circle"
Rectangle *pRectangle = new Rectangle; // static type = "pointer to Rectangle"
Each pointer has a static type based on the way it's declared.
The static type determines which member functions can be called.
Each pointer has a dynamic type based on the type of object to
which it currently points. The dynamic type determines which class'
implementation of the member function will be used.
pShape = pCircle; // pShape's dynamic type is "pointer to Circle"
pShape->Draw( ); // calls Circle::Draw( ) and draws a Circle
pShape = pRectangle; // pShape's dynamic type is now "pointer to Rectangle"
pShape->Draw ( ); // calls Rectangle::Draw( ) and draws a Rectangle
// an array of Shape (Base class) pointers
Shape *shapes[3];
shapes[0] = new Circle;
shapes[1] = new Rectangle;
shapes[2] = new Triangle;
for (int s = 0; s < 3; s++)
shapes[s]->Draw( );
Last Modified: Monday, 28-Aug-2006 10:16:04 EDT