Re: Constructors with long parameter list
mkluwe@gmail.com () wrote (abridged):
I have several classes which happened to take a long (say 20 or more)
list of parameters in their constructors.
[...]
2) Pack the parameters in a struct (in fact, they are already taken
from one which has to be reordered slightly) and use these struct in
the initialization list. Con: Parameter are not the same for the
classes (but nearly), so I would consider a struct A_param for class A
That sounds OK. You can use nested classes:
class NumberCruncher {
public:
struct Params {
double x;
double y;
double a;
// .. 17 more instance variables.
};
NumberCruncher( const Params &p ) : p(p) {}
//...
private:
Params p;
};
int main() {
NumberCruncher::Params p;
p.x = 1;
p.y = 27;
p.a = 42;
// ...
NumberCruncher( p );
//...
}
I imagine the compiler would optimise this pretty well. To my mind the
main drawback is that you have no static guarantee that all the variables
are assigned to, but you can check at runtime instead and that will
probably be enough.
The "uglyness factor" remains. I'd be glad to hear your opinion.
It sounds like initialising your class is intrinsically complex. I have an
intuitive sense of the "value" of code, whereby simple things should be
simple but it is OK for complex things to take more code. Sometimes
difficult things become easier if you recognise that they are difficult
and grant them the code space they need.
In this case trying to get the entire initialisation into a single line
may be a mistake. I think it's acceptable to devote some infrastructure to
it, in the form of helper classes like NumberCruncher::Params and the many
assignment statements. Obviously if there are some good abstractions and
ways of combining groups of variables into objects, then take advantage of
that, but I gather from earlier messages in the thread that there aren't
any.
Con: Parameter are not the same for the classes (but nearly)
I would also consider warping the design to make the parameter lists the
same. If it led to a major simplification of the code, and more efficient
runtime (because less need to reorder the data), then it might be worth
the loss of theoretical purity.
This could lead to you passing a single monster parameter block around by
reference, in which case it would become equivalent to a globals, which
would be bad - but you don't have to do that. Passing by value would
reduce the scope for different classes to interfere with each other. You
could also use access controls if you wanted. Eg:
struct AllParams {
double x;
double y;
double a;
double b;
// .. 16 more instance variables.
};
struct Params_A : private AllParams {
friend struct Params_B;
using AllParams::x;
using AllParams::y;
using AllParams::a;
using AllParams::b;
};
struct Params_B : private AllParams {
Params_B( const Params_A &p ) : AllParams(p) {}
using AllParams::x;
using AllParams::y;
};
The idea is that Params_B provides access to x and y but not a and b. The
AllParams common base class facilitates conversions between different
parameter blocks, because they all have the same contents and layout at
heart.
Even without the common base class, having the parameter block objects
provide conversions to and from other parameter block objects may be a
neat factoring of the code. It keeps much of the variable-juggling out of
the main number crunching classes.
3) Pack the parameters in a struct and use this internally instead of
all the private members.
You can also use inheritance:
class NumberCruncher : private Params_A {
public:
NumberCruncher( const Params_A &p ) : Params_A(p) {}
};
which lets you access the variables as "x" rather than "p.x".
-- Dave Harris, Nottingham, UK.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]