Let's say we already have a hierarchy of classes, e.g.
class Shape { virtual void get_area() = 0; };
class Square : Shape { ... };
class Circle : Shape { ... };
etc.
Now let's say that I want to (effectively) add a virtual draw() = 0
method to Shape
with appropriate definitions in each sub-class. However, let's say I want to do this without modifying those classes (as they are part of a library that I don't want to change).
What would be the best way to go about this?
Whether or not I actually "add" a virtual
method or not is not important, I just want polymorphic behaviour given an array of pointers.
My first thought would be to do this:
class IDrawable { virtual void draw() = 0; };
class DrawableSquare : Square, IDrawable { void draw() { ... } };
class DrawableCircle : Circle, IDrawable { void draw() { ... } };
and then just replace all creations of Square
s and Circle
s with DrawableSquare
s and DrawableCircle
s, respectively.
Is that the best way to accomplish this, or is there something better (preferably something that leaves the creation of Square
s and Circle
s intact).
Thanks in advance.
Derived classes do not have to implement all virtual functions themselves. They only need to implement the pure ones. That means the Derived class in the question is correct.
It is not mandatory for the derived class to override (or re-define the virtual function), in that case, the base class version of the function is used.
Virtual functions in a base class must be defined unless they are declared using the pure-specifier.
Redefining the virtual function in the derived class is optional, but it needs to be defined in the base class. The function call resolving is done at run-time. You can create a virtual destructor but not a constructor.
(I do propose a solution down further... bear with me...)
One way to (almost) solve your problem is to use a Visitor design pattern. Something like this:
class DrawVisitor
{
public:
void draw(const Shape &shape); // dispatches to correct private method
private:
void visitSquare(const Square &square);
void visitCircle(const Circle &circle);
};
Then instead of this:
Shape &shape = getShape(); // returns some Shape subclass
shape.draw(); // virtual method
You would do:
DrawVisitor dv;
Shape &shape = getShape();
dv.draw(shape);
Normally in a Visitor pattern you would implement the draw
method like this:
DrawVisitor::draw(const Shape &shape)
{
shape.accept(*this);
}
But that only works if the Shape
hierarchy was designed to be visited: each subclass implements the virtual method accept
by calling the appropriate visitXxxx
method on the Visitor. Most likely it was not designed for that.
Without being able to modify the class hierarchy to add a virtual accept
method to Shape
(and all subclasses), you need some other way to dispatch to the correct draw
method. One naieve approach is this:
DrawVisitor::draw(const Shape &shape)
{
if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visitSquare(*pSquare);
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visitCircle(*pCircle);
}
// etc.
}
That will work, but there is a performance hit to using dynamic_cast that way. If you can afford that hit, it is a straightforward approach that is easy to understand, debug, maintain, etc.
Suppose there was an enumeration of all shape types:
enum ShapeId { SQUARE, CIRCLE, ... };
and there was a virtual method ShapeId Shape::getId() const = 0;
that each subclass would override to return its ShapeId
. Then you could do your dispatch using a massive switch
statement instead of the if-elsif-elsif of dynamic_cast
s. Or perhaps instead of a switch
use a hashtable. The best case scenario is to put this mapping function in one place, so that you can define multiple visitors without having to repeat the mapping logic each time.
So you probably don't have a getid()
method either. Too bad. What's another way to get an ID that is unique for each type of object? RTTI. This is not necessarily elegant or foolproof, but you can create a hashtable of type_info
pointers. You can build this hashtable in some initialization code or build it dynamically (or both).
DrawVisitor::init() // static method or ctor
{
typeMap_[&typeid(Square)] = &visitSquare;
typeMap_[&typeid(Circle)] = &visitCircle;
// etc.
}
DrawVisitor::draw(const Shape &shape)
{
type_info *ti = typeid(shape);
typedef void (DrawVisitor::*VisitFun)(const Shape &shape);
VisitFun visit = 0; // or default draw method?
TypeMap::iterator iter = typeMap_.find(ti);
if (iter != typeMap_.end())
{
visit = iter->second;
}
else if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visit = typeMap_[ti] = &visitSquare;
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visit = typeMap_[ti] = &visitCircle;
}
// etc.
if (visit)
{
// will have to do static_cast<> inside the function
((*this).*(visit))(shape);
}
}
Might be some bugs/syntax errors in there, I haven't tried compiling this example. I have done something like this before -- the technique works. I'm not sure if you might run into problems with shared libraries though.
One last thing I'll add: regardless of how you decide to do the dispatch, it probably makes sense to make a visitor base class:
class ShapeVisitor
{
public:
void visit(const Shape &shape); // not virtual
private:
virtual void visitSquare(const Square &square) = 0;
virtual void visitCircle(const Circle &circle) = 0;
};
What you're describing is somewhat like the decorator pattern. Which is very suitable to change runtime behaviour of existing classes.
But I don't really see how to implement your practical example, if shapes have no way to be drawn, then there's no way to change drawing behaviour at runtime either...
But I suppose this is just a very simplified example for stackoverflow? If all the basic building blocks for the desired functionality are available, then implementing the exact runtime behaviour with such a pattern is certainly a decent option.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With