Hello!
First of all please forgive me for not posting a compilable snippet,
but rather a simplified piece of code with the unimportant
details left out.
Let's say I have two classes 'box_shape' and 'cylinder_shape' that
derive from a common base class 'shape', more or less like this:
namespace Shapes {
int SHA_UNDEFINED=-1;
int SHA_BOX=0;
int SHA_CYLINDER=1;
class shape {
public:
shape();
virtual ~shape();
int virtual what_shape() const {
return SHA_UNDEFINED;
}
void virtual move_shape(/*...*/) {
/*...*/
}
/* some more methods common to all shapes*/
};
class box_shape : public shape {
public:
box_shape();
int what_shape() const {
return SHA_BOX;
}
void move_shape(/*...*/) {
shape::move_shape(/*...*/);
/* do some stuff specific to box_shape */
}
vector<double> box_corners() const { // returns box corners
/*...*/
}
};
class cylinder_shape : public shape {
public:
cylinder_shape();
int what_shape() const {
return SHA_CYLINDER;
}
void move_shape(/*...*/) {
shape::move_shape(/*...*/);
/* do some stuff specific to cylinder_shape */
}
double cylinder_height() const { // returns cylinder height
/*...*/
}
};
} // of namespace Shapes
Later on I have a class called 'system_geometry' which
contains a member 'region', which is a shape, either a box-like or
a cylindrical shape. Since I don't know in advance which
particular descendant of 'shape' this region is, I decided
to use a pointer to shape, like this:
class system_geometry {
public:
/* ... lots of stuff here ... */
Shapes::shape *region;
}
and I initialize it like that:
if(user_wants_a_box) region=new Shapes::box_shape;
else if(user_wants_a_cylinder) region=new Shapes::cylinder_shape;
Thanks to polymorphism I can now have calls like
region->what_shape() or region->move_shape() which
dynamically resolve to methods of appropriate descendant classes.
That's all right up to this point. There are some places in
my code, however, when I need to call some methods that are
specific to the derived classes, something akin to:
switch(region.what_shape()) {
case SHA_BOX:
// this branch now needs to call region->box_corners();
break;
case SHA_CYLINDER:
// this branch now needs to call region->cylinder_height();
break;
default:
throw EInternalError("Wrong shape");
}
The problem is, of course, that neither box_corners() nor
cylinder_height() are defined for the base class 'shape'.
What should I do in this situation? I've thought up two
solutions. One, possibly ugly, is to add all the names of
all methods of the derived classes to the base class as
pure virtual methods. That way 'shape' will formally have
all the required methods. I don't like that solution.
The other solution I've been pondering is via dynamic_cast.
Will it be all right to do something like:
switch(region.what_shape()) {
case SHA_BOX:
box_shape *region_as_box=dynamic_cast<box_shape*>(region);
// now this branch can use region_as_box->box_corners()
break;
case SHA_CYLINDER:
cylinder_shape *region_as_cylinder=
dynamic_cast<cylinder_shape*>(region);
// now this branch can use region_as_cylinder->cylinder_height();
break;
default:
throw EInternalError("Wrong shape");
}
I have doubts because I recall that casting base --> derived
is not a good idea. On the other hand I can be sure that 'region'
definitely points to an instance of a derived class, not the base.
And I guess I'm trying to cast base* --> derived*, but I'm a little
confused here.
Is this approach OK? If not, how can I solve this problem?
TIA,
- J.