Question:
Suppose I’ve got the following class hierarchies:
Surface_PlotContourSurface_Plot, child of Surface_PlotSurface_DataGridSurface_Data, child of Surface_DataSurface_Plot contains a Surface_Data object, ContourSurface_Plot contains a GridSurface_Data object.What is the most elegant way to arrange the data members of the
Surface_Plot
hierarchy so that the Surface_Plot
class can access the GridSurface_Data
object’s Surface_Data
behaviors, and ContourSurface_Plot
can access its extended behaviors? My approach has been to make the xxxData
objects pointers, and to have both a GridSurface_Data
and Surface_Data
pointer referencing the same data member.This route to a solution seems terribly ugly and error-prone. Furthermore, my real hierarchies are much longer, resulting in a plethora of redundant pointers of increasingly sophisticated types.
There must be a cleaner way!
Answer:
You are right ? having two pointers is a problem and could end up becominga maintenance nightmare. The trick is to use the right type in the derived class to access the extended behavior. Consider:
class Surface_Plot{protected: Surface_Data *data_;};class ContourSurface_Plot : public Surface_Plot{public: void useExtendedOperation () { GridSurface_Data * g = dynamic_castThat’s the basic idea. If the(data); if(g) g->doSomething (); }};
GridSurface_Data
needs to be usedin a lot of places, it might be worthwhile to incorporate it asa data member and initialize it in the constructor of ContourSurface_Plot
.This is similar to your multiple pointer solution. In this case you want to keep a reference in the derived class to prevent accidental deletion.In an implementation I would move the burden of maintaining this extra pointer to another class that will do the cast, etc., as in:
templateand then inheritclass Surface_Data_Container : public Surface_Plot{protected: Surface_Data_Container() : data_(dynamic_cast (Surface_Plot::data_)) { assert(data_ != 0); } T *data_;};
ContourSurface_Plot
from Surface_Data_Container
:class ContourSurface_Plot : public Surface_Data_Container{public: void useExtendedOperation () { data_->doSomething (); // accesses Surface_Data_Container ::data_ which is the // right type.. }};