Adding virtual functions without changing the source classes

Say we already have a class hierarchy, for example

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 the corresponding definitions in each subclass. However, let me say that I want to do this without changing these classes (since they are part of a library that I do not want to change).

What would be the best way to do this?

Regardless of whether I really add a virtual method or not, this is not important, I just want the polymorphic behavior to be specified by an array of pointers.

My first thought was this:

 class IDrawable { virtual void draw() = 0; }; class DrawableSquare : Square, IDrawable { void draw() { ... } }; class DrawableCircle : Circle, IDrawable { void draw() { ... } }; 

and then just replace all the Square and Circle creations with DrawableSquare and DrawableCircle s respectively.

This is the best way to accomplish this, or there is something better (preferably something that leaves the Square and Circle creation intact).

Thanks in advance.

+6
c ++ polymorphism oop
source share
3 answers

(I propose a solution further ... bear with me ...)

One way to (almost) solve your problem is to use a visitor design template. Something like that:

 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:

 Shape &shape = getShape(); // returns some Shape subclass shape.draw(); // virtual method 

You would do:

 DrawVisitor dv; Shape &shape = getShape(); dv.draw(shape); 

Typically, in a visitor template, you implement the draw method as follows:

 DrawVisitor::draw(const Shape &shape) { shape.accept(*this); } 

But this only works if the Shape hierarchy was intended to be visited: each subclass implements a virtual accept method by calling the corresponding visitXxxx method for Visitor. Most likely, it was not intended for this.

Without the ability to change the class hierarchy to add a virtual accept method to Shape (and all subclasses), you will need another way to send the correct draw method. One naive principle is as follows:

 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. } 

This will work, but it uses the dynamic use of dynamic_cast. If you can afford this hit, this is a simple approach that is easy to understand, debug, maintain, etc.

Suppose that there is a list of all types of shapes:

 enum ShapeId { SQUARE, CIRCLE, ... }; 

and there was a virtual method ShapeId Shape::getId() const = 0; so that each subclass redefines its ShapeId . You can then send your newsletter using the massive switch instead of if-elsif-elsif of dynamic_cast s. Or perhaps use a hash table instead of switch . The best scenario is to put this mapping function in one place so that you can identify multiple visitors without repeating the display logic every time.

So you probably don't have a getid() method. Very sorry. What is another way to get an identifier unique to each type of object? RTTI It is not necessarily elegant or reliable, but you can create a hash table with type_info pointers. You can build this hash table 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); } } 

There may be some errors / syntax errors there, I have not tried compiling this example. I used to do something similar - the technique works. I'm not sure if you might run into problems with shared libraries.

The last thing I will add: no matter how you decide to send, it probably makes sense to make the base class of the visitor:

 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; }; 
+5
source share

What you are describing is somewhat reminiscent of a decorator pattern. This is very convenient for changing the behavior of executable classes at runtime.

But I really donโ€™t see how to implement your practical example, if the forms cannot be drawn, then there is no way to change the drawing behavior at runtime ...

But I suppose this is just a simplified example for stackoverflow? If all the basic building blocks for the desired functionality are available, then implementing accurate runtime behavior with such a template is certainly a worthy option.

+3
source share

The off-wall solution that you might want to consider, depending on the circumstances, is to use patterns to give you polymorphic compile-time behavior. Before you say anything, I know that this will not give you the traditional polymorphism at runtime, so it may not be useful, but depending on the limitations of the environment in which you work, it may be useful:

 #include <iostream> using namespace std; // This bit a bit like your library. struct Square{}; struct Circle{}; struct AShape{}; // and this is your extra stuff. template < class T > class Drawable { public: void draw() const { cout << "General Shape" << endl; } }; template <> void Drawable< Square >::draw() const { cout << "Square!" << endl; }; template <> void Drawable< Circle >::draw() const { cout << "Circle!" << endl; }; template < class T > void drawIt( const T& obj ) { obj.draw(); } int main( int argc, char* argv[] ) { Drawable<Square> a; Drawable<Circle> b; Drawable<AShape> c; a.draw(); // prints "Square!" b.draw(); // prints "Circle!" c.draw(); // prints "General Shape" as there no specific specialisation for an Drawable< AShape > drawIt(a); // prints "Square!" drawIt(b); // prints "Circle!" drawIt(c); // prints "General Shape" as there no specific specialisation for an Drawable< AShape > } 

The drawIt() method is probably the key point here, since it represents the general behavior for any class that satisfies the requirement of the draw() method. Watch out for code bloat here, though, as the compiler will instantiate a separate method for each type passed.

This can be useful in situations where you need to write a single function to work with many types that do not have a common base class. I know this is not a question that you asked, but I thought I would drop it as an alternative.

0
source share

All Articles