Thanks But No Thanks, C++Reading over the last few paragraphs, the astute programmer will notice an interesting point: At no time did we actually need runtime polymorphism. It helped us write our DrawAShapeOverAndOver() function by letting us write a single function that would work for all classes derived from Shape, but in each case the run-time lookup could have been done at compile-time. Bearing this in mind, let's approach polymorphism again, but this time with more caution. We won't be making the DrawOutline() method virtual again, since so far that has done us no good at all. Instead, let's rewrite DrawAShapeOverAndOver() as a templated function. This way we are not forced to write both DrawAShapeWhichIsARectangleOverAndOver() and DrawAShapeWhichIsACircleOverAndOver().
template<typename ShapeType>
void DrawAShapeOverAndOver(ShapeType* myShape)
{
for(int i=0; i<10000; i++)
{
myShape->DrawOutline();
}
}
Rectangle *myRectangle = new Rectangle;
DrawAShapeOverAndOver(myRectangle);
delete myRectangle;
Hey! Now we're getting somewhere! We can pass in any kind of Shape to DrawAShapeOverAndOver(), just like before, except this time there is no runtime checking of myShape's type! Interestingly enough, Rectangle and Circle don't even have to be derived from Shape. They just have to be classes with a DrawOutline() function. Making Our Lives More DifficultLet's go back to our original example, but this time let's make more use of the other features of subclassing. After all, derived classes and base classes with no private members, nontrivial constructors, or internal calls of virtual functions are a rather severe oversimplification of subclassing. Let's also supply an actual implementation of DrawOutline() and DrawFill(), albeit using a completely fictional Graphics object that will nevertheless allow us to illustrate how functions in derived classes may use functions in base classes. Now, let's pull out the big guns.
class Shape
{
public:
Shape(const Point &initialLocation,
const std::string &initialOutlineColor,
const std::string &initialFillColor) :
location(initialLocation),
outlineColor(initialOutlineColor),
fillColor(initialFillColor)
{
}
virtual ~Shape()
{
}
virtual void DrawOutline() const = 0;
virtual void DrawFill() const = 0;
void SetOutlineColor(const std::string &newOutlineColor)
{
outlineColor = newOutlineColor;
}
void SetFillColor(const std::string &newFillColor)
{
fillColor = newFillColor;
}
void SetLocation(const Point & newLocation)
{
location = newLocation;
}
const std::string &GetOutlineColor() const
{
return outlineColor;
}
const std::string &GetFillColor() const
{
return fillColor;
}
const Point &GetLocation() const
{
return location;
}
void DrawFilled() const
{
DrawOutline();
DrawFill();
}
private:
std::string outlineColor;
std::string fillColor;
Point location;
};
class Rectangle : public Shape
{
public:
Rectangle(const Point &initialLocation,
const std::string &initialOutlineColor,
const std::string &initialFillColor(),
double initialHeight,
double initialWidth) :
Shape(initialLocation, initialOutlineColor,
initialFillColor),
height(initialHeight),
width(initialWidth)
{
}
virtual ~Rectangle()
{
}
virtual void DrawOutline() const
{
Graphics::SetColor(GetOutlineColor());
Graphics::GoToPoint(GetLocation());
Graphics::DrawRectangleLines(height, width);
}
virtual void DrawFill() const
{
Graphics::SetColor(GetOutlineColor());
Graphics::GoToPoint(GetLocation());
Graphics::DrawRectangleFill(height, width);
}
void SetHeight(double newHeight)
{
height = newHeight;
}
void SetWidth(double newWidth)
{
width = newWidth;
}
double GetHeight() const
{
return height;
}
double GetWidth() const
{
return width;
}
private:
double height;
double width;
};
class Circle : public Shape
{
public:
Circle(const Point &initialLocation,
const std::string &initialOutlineColor,
const std::string &initialFillColor,
double initialRadius) :
Shape(initialLocation, initialOutlineColor,
initialFillColor),
radius(initialRadius)
{
}
virtual ~Circle()
{
}
virtual void DrawOutline() const
{
Graphics::SetColor(GetOutlineColor());
Graphics::GoToPoint(GetLocation());
Graphics::DrawCircularLine(radius);
}
virtual void DrawFill() const
{
Graphics::SetColor(GetOutlineColor());
Graphics::GoToPoint(GetLocation());
Graphics::DrawCircularFill(radius);
}
void SetRadius(double newRadius)
{
radius = newRadius;
}
double GetRadius() const
{
return radius;
}
private:
double radius;
};
Whew! Let's see what we added there. First of all, Shape objects now have data members. All Shape objects have a location, and an outlineColor and a fillColor. In addition, Rectangle objects have a height and a width, and Circle objects have a radius. Each of these members has corresponding getter and setter functions. The most important new addition is the DrawFilled() method, which draws both the outline and the fill in one step by delegating these methods to the derived class. |
|