Here is the context: A Model
has a (pointer to) Parameter
and an output
. Model
and Parameter
are abstract classes. We use pointers of type Model*
to manipulate various derived (concrete) classes of Model
, whose pointers to Parameter
dynamically point to instances of various derived (concrete) classes of Parameter
.
Here is a simplified version of the classes as an example. I know new
should be avoided or at least followed by delete
, but I omitted off-topic lines of code (such as destructors).
// Abstract classes
class Parameter {
public:
virtual void reinitialize() = 0;
};
class Model{
public:
Model(Parameter *argt){ ptr_param = argt; }
virtual void computeModelOutput() = 0;
double output;
Parameter *ptr_param;
};
// Concrete classes
class ACertainKindOfParameter : public Parameter{
public:
ACertainKindOfParameter(int argt){ value = argt; }
virtual void reinitialize(){ value = 1; }
int value;
};
class ACertainKindOfModel : public Model{
public:
ACertainKindOfModel(int argt) : Model(new ACertainKindOfParameter(argt)){}
virtual void computeModelOutput(){
output = 10.0 + (double)(static_cast<ACertainKindOfParameter*>(ptr_param)->value);
}
};
int main(){
ACertainKindOfModel myModel{5};
Model *ptr_model = &myModel;
ptr_model->computeModelOutput();
std::cout << ptr_model->output << std::endl; // 15
}
What bothers me in this code is that ACertainKindOfModel
has no direct access to value
, so I apparently need to use static_cast
. A real Model
would of course have a vector of e.g. 50 Parameter
s, not just one, so that would mean 50 static_cast
each time the output
is computed (or any other action relying on parameters). That does not look like a good practice to me, but I may be wrong. Do you see any flaw in the design?
Note: I thought of making Parameter
a class template, but it doesn't seem to be a valid option because the methods of Parameter
differ deeply when different types of value
are considered. In the simple example above, value
is of type int
, but in another class derived from Parameter
it could be of user-defined type, e.g. Color
with only three possible values R
, G
and B
, and reinitialize()
would be very different than value = 1
. A virtual getter()
in Parameter
would be great but would not work either, because of a conflicting return type in the redefinition.
There are several approaches to make this cleaner. If
Model
does not need access toptr_param
, you could remove that fromModel
and store it within each derived class, with the correct type.Or you can encapsulate the
static_cast
in a getter function within each model class:You can combine the two techniques. Define the parameter within the derived model class, and use a covariant return type to allow the base
Model
class access. WithinModel
, declare a getter:Then, within each model, declare a covariant override:
which assumes ptr_param is declared within
ACertainKindOfModel
. If it isn't you'll need to apply thestatic_cast
as above.Or you can save the result of the
static_cast
within thecompute
function to avoid having to use it multiple times.