Should you perform complex tasks in the constructor?

C

Chicken McNuggets

I've seen various arguments against this primarily centring on the fact
that the only way to return errors in a constructor is to throw an
exception. Of course exceptions seem to be a somewhat controversial
subject in the C++ community so I'll try and avoid touching on that.

But I have a couple of classes that are created when the program
launches and are freed just before the program terminates. It makes
sense for me to put a lot of logic in the constructor as these classes
are initialised from data in different configuration files which are
passed as command line arguments.

Is there any reason I shouldn't put all the file loading / reading /
storing of data in the constructor? Or would you not consider this a
problem? For reference I'm using libxml2 for reading the data in the
configuration files.
 
V

Victor Bazarov

I've seen various arguments against this primarily centring on the fact
that the only way to return errors in a constructor is to throw an
exception. Of course exceptions seem to be a somewhat controversial
subject in the C++ community so I'll try and avoid touching on that.

But I have a couple of classes that are created when the program
launches and are freed just before the program terminates. It makes
sense for me to put a lot of logic in the constructor as these classes
are initialised from data in different configuration files which are
passed as command line arguments.

Is there any reason I shouldn't put all the file loading / reading /
storing of data in the constructor? Or would you not consider this a
problem? For reference I'm using libxml2 for reading the data in the
configuration files.

IMHO your question is a bit too general, which basically means the
answer to it is "there can be lots of things". For instance, putting
too much processing into constructors of some global data objects can
stretch the startup of a UI-driven application, and that's usually
frowned upon. At the same time if the application cannot function
without those objects fully set up, then it has to complete the
initialization whether it's in the constructor or elsewhere, so in that
case it probably doesn't really matter... What other reasons are there?
I can probably think of several. What if after some time you decide
to introduce some other initialization, and it has to be done before
your initial procedure? Do you create another global object and
construct that (and put the new initialization there)? You can run into
the problems connected to the order of things being initialized at
different times on different runs. What if that initialization has to
be conditional or have parameters that it reads from some other place?
So, think of maintenance of your code, not just of writing it... There
are probably other ones as well.

V
 
G

Greg Martin

I've seen various arguments against this primarily centring on the fact
that the only way to return errors in a constructor is to throw an
exception. Of course exceptions seem to be a somewhat controversial
subject in the C++ community so I'll try and avoid touching on that.

But I have a couple of classes that are created when the program
launches and are freed just before the program terminates. It makes
sense for me to put a lot of logic in the constructor as these classes
are initialised from data in different configuration files which are
passed as command line arguments.

Is there any reason I shouldn't put all the file loading / reading /
storing of data in the constructor? Or would you not consider this a
problem? For reference I'm using libxml2 for reading the data in the
configuration files.

In cases where it makes sense I set error variables to be tested, just
like a returned value, after the constructor completes.

e.g.
File *f = new File (path);
if (!f->success ()) {
std::cerr << f->error () << std::endl;
// cleanup
}
 
Ö

Öö Tiib

I've seen various arguments against this primarily centring on the fact
that the only way to return errors in a constructor is to throw an
exception. Of course exceptions seem to be a somewhat controversial
subject in the C++ community so I'll try and avoid touching on that.

It is widespread misconception that the only way to fail construction is to
throw exceptions. Alternative is to construct a "bad" object. To make it
possible to construct "bad" object the class has to be designed to allow
such states (like "obsolete", "failed", "unknown", "broken", "invalid",
"unusable", etc.). Such states may be useful (when natural) or may cause
complications (when otherwise pointless). When there are natural "bad"
states then it is not needed to throw exceptions from constructors.
But I have a couple of classes that are created when the program
launches and are freed just before the program terminates. It makes
sense for me to put a lot of logic in the constructor as these classes
are initialised from data in different configuration files which are
passed as command line arguments.

Actually all the objects should be valid after construction. If there is
some "half-made" or "uninitialized" state after construction then that
is generally even less natural than outright "failed" states.
Is there any reason I shouldn't put all the file loading / reading /
storing of data in the constructor? Or would you not consider this a
problem? For reference I'm using libxml2 for reading the data in the
configuration files.

One issue that I have seen is when static object or static data
member of class has complex constructor that may throw. If it throws
then the program crashes when starting. Other issue that I have seen
with long-running constructors is when the program later realizes that
the object is not needed for anything aftrer all.

Generally I tend to prefer complex constructors. For example when
it is some "request" class and these have to be always sent somewhere
anyway then I usually tend to merge its construction and sending both
into constructor. That makes it simpler to use the class.
 
C

Chicken McNuggets

IMHO your question is a bit too general, which basically means the
answer to it is "there can be lots of things". For instance, putting
too much processing into constructors of some global data objects can
stretch the startup of a UI-driven application, and that's usually
frowned upon. At the same time if the application cannot function
without those objects fully set up, then it has to complete the
initialization whether it's in the constructor or elsewhere, so in that
case it probably doesn't really matter... What other reasons are there?
I can probably think of several. What if after some time you decide
to introduce some other initialization, and it has to be done before
your initial procedure? Do you create another global object and
construct that (and put the new initialization there)? You can run into
the problems connected to the order of things being initialized at
different times on different runs. What if that initialization has to
be conditional or have parameters that it reads from some other place?
So, think of maintenance of your code, not just of writing it... There
are probably other ones as well.

V

I'll try and be more specific.

Basically my application (well it is actually a Unix daemon) has one
global configuration file in XML format. This lists a group of project
specific configuration files.

The global configuration file is read by one class and this then loads
all of the project specific configuration settings into a std::vector of
project specific classes.

The program then forks for each specific project configuration file and
uses those configurations for things such as which port to listen on and
so forth.

Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.
 
C

Chicken McNuggets

In cases where it makes sense I set error variables to be tested, just
like a returned value, after the constructor completes.

e.g.
File *f = new File (path);
if (!f->success ()) {
std::cerr << f->error () << std::endl;
// cleanup
}

Yeah I considered this approach but it has a tendency to expose the
inner workings of the class if you are not careful, and I'd like to make
sure that re-writing the class in the future to take into account more
configuration options does not lead to a re-write of critical pieces of
the code since this class is of crucial importance to the rest of the
program.
 
C

Chicken McNuggets

One issue that I have seen is when static object or static data
member of class has complex constructor that may throw. If it throws
then the program crashes when starting. Other issue that I have seen
with long-running constructors is when the program later realizes that
the object is not needed for anything aftrer all.

Thankfully the last point is never going to happen so that shouldn't be
a concern.
Generally I tend to prefer complex constructors. For example when
it is some "request" class and these have to be always sent somewhere
anyway then I usually tend to merge its construction and sending both
into constructor. That makes it simpler to use the class.

This is useful and is my primary reason for my current configuration. I
basically want the classes to handle themselves with very little outside
interaction required.
 
S

Stuart

On 01/11/13 Chicken McNuggets wrote:
[snip]
Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.

It looks as if you put the whole logic into the constructor of a single
class. That seems a bit odd to me. Why do you need to create such an
object at all? Maybe you should use just a bunch of functions, it sounds
as if the problem domain does not really demand an object-oriented
approach. Ask yourself this: Will anybody use my class in a different
way in order to solve similar problems? If your answer is "No, this
class makes only sense in this particular project and I only need to
create a single instance of my class" than you can drop the whole class.

Regards,
Stuart
 
G

Greg Martin

Yeah I considered this approach but it has a tendency to expose the
inner workings of the class if you are not careful, and I'd like to make
sure that re-writing the class in the future to take into account more
configuration options does not lead to a re-write of critical pieces of
the code since this class is of crucial importance to the rest of the
program.

To me it's a question of the reasonable expectations of the programmer
using the class. In socket/file io experienced programmers know what is
under the hood of the class and expect to be able to test/catch errors.
My preference is for testing in those cases because of efficiency
concerns. Exposing the workings is a matter of what gets called "least
surprise". Alternatively, exposing open/read etc. with the usual return
values and allowing the programmer to test works - the programmer can
refer to system documentation.

For instance, with a non-blocking fd under unix you can provide a method
to test for EAGAIN and EAGAIN | EWOULDBLOCK or return the result of the
system call directly and let the programmer worry about it.

Here's an example from a non-blocking socket. It doesn't look much
different then if written without OO but the functionality is
encapsulated - still it should be clear, to someone who has used
non-blocking socket io in unix, what is happening. (the code is from an
accept loop using the epoll api - hence the break).

Socket insock = ss->accept ();

if (insock.error ()) {
if (insock.errorAgain () || insock.errorWouldBlock ()) {
break;
} else {
std::cerr << insock.errorMsg () << std::endl;
break;
}
}
insock.setNonBlocking ();
 
Ö

Öö Tiib

Thankfully the last point is never going to happen so that shouldn't be
a concern.

Never say "never". It is never going to happen in optimal design. In
practice no design is optimal. I will try to bring some example of it
.... hmm.

Lets say (in light of previous example of mine) that there is number (x)
of complex objects that the software has to monitor with requests. Each
object has number (y) of complex parameters, setting all the space of
requesting parameters and ability to interpret the results of requests
up for monitoring will take some (x*y) time and space.

It may be that actually values of only few (z, z is smaller than y)
parameters are needed for routine monitoring. All (y) are needed only
on corner case for reasons when more attention toward particular object
is needed. Preparing less (x*z) parameters is certainly cheaper, but it
might be harder to develop such constructor than one that prepares all
right away.

The difference between costs (x*y-x*z) is usually not immediately
apparent. Often it is actually insignificant. Added complexity by
preparing subset may add other costs and lower the robustness of the design..
It can not be said what (if anything) in here "can be issue" or "hard to
notice" without profiling the whole thing.

That means effort. No human can deliver endless effort of weighting such
odds. On the contrary. Numerous people will name optimizations that give
insignificant improvement gained with measurable effort as "preliminary".
They mean preliminary in sense that applied without measuring. Preparing
the alternatives and measuring is however itself an effort. Therefore no
program is ever optimal.
 
C

Chicken McNuggets

On 01/11/13 Chicken McNuggets wrote:
[snip]
Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.

It looks as if you put the whole logic into the constructor of a single
class. That seems a bit odd to me. Why do you need to create such an
object at all? Maybe you should use just a bunch of functions, it sounds
as if the problem domain does not really demand an object-oriented
approach. Ask yourself this: Will anybody use my class in a different
way in order to solve similar problems? If your answer is "No, this
class makes only sense in this particular project and I only need to
create a single instance of my class" than you can drop the whole class.

Regards,
Stuart

I'm actually a C programmer making the switch to C++ so my original
method was just to have a plain old C struct and a couple of functions
to handle it but I wanted to use idiomatic C++ so that I got used to it.

To be honest using the C approach is easier, faster (in terms of
development time) and in my opinion clearer but since this code will be
used by others who perhaps are not used to the C way of doing things I
was a little concerned that it might put them off.
 
S

Stuart

[snip]
Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.
It looks as if you put the whole logic into the constructor of a single
class. That seems a bit odd to me. Why do you need to create such an
object at all? Maybe you should use just a bunch of functions, it sounds
as if the problem domain does not really demand an object-oriented
approach. Ask yourself this: Will anybody use my class in a different
way in order to solve similar problems? If your answer is "No, this
class makes only sense in this particular project and I only need to
create a single instance of my class" than you can drop the whole class.


I'm actually a C programmer making the switch to C++ so my original
method was just to have a plain old C struct and a couple of functions
to handle it but I wanted to use idiomatic C++ so that I got used to it.

To be honest using the C approach is easier, faster (in terms of
development time) and in my opinion clearer but since this code will be
used by others who perhaps are not used to the C way of doing things I
was a little concerned that it might put them off.

I would recommend to leave the code the way it is (IOW, a regular C
function) unless there emerges a good reason why it should be made an
object. One good reason to make it in object would be RAII. Another
would be to hide some implementation details from the users but that
only is a good reason if the same cannot be achieved with a simple
module (as in your case).

If you only ever need one object of a class, chances are high that a
regular module would suffice as well. If you think that your code is
alright the way it is, then leave at that. Only refactor if there is a
good reason to do so or else you'll end up trying to find the optimal
solution for a problem whose problem space does not have optimums but
only equivally good working alternatives. Else you might end up looking
for an optimal solution in a problem space that simply has no optimal
solutions but only equally good ones.

BTW, there is nothing wrong at all with plain old functions. I'd rather
have a clean consistent function than a singleton object that has no
inner state and only exists because everything has to be object-oriented.

Regards,
Stuart
 
S

Szabolcs Ferenczi

I've seen various arguments against this primarily centring on the fact

that the only way to return errors in a constructor is to throw an

exception.

Another argument can be testability, especially if you think about unit testing.

The setting up of such an object is complex too and therefore it is error prone and fragile.

My preference is using the constructor just for initializing the members to their default values.
 
J

Jorgen Grahn

On 01/11/13 Chicken McNuggets wrote:
[snip]
Basically the way the program works now by simply instantiating a new
global configuration object all the other objects are created
automatically and are populated with the correct data. No need for more
than a single statement (excluding try / catch etc).

Hopefully that gives you a better idea of what I am trying to achieve.

It looks as if you put the whole logic into the constructor of a single
class. That seems a bit odd to me. Why do you need to create such an
object at all? Maybe you should use just a bunch of functions, it sounds
as if the problem domain does not really demand an object-oriented
approach.

Why not? If the "global configuration object" isn't an object, what
should it be? There's Unix getopt() which just feeds you the
parameters one by one, but I doubt that approach is good with XML.

I disagree (although perhaps I misunderstand you). Classes are not
just a tool for software reuse, if that's what you're saying.
I'm actually a C programmer making the switch to C++ so my original
method was just to have a plain old C struct and a couple of functions
to handle it but I wanted to use idiomatic C++ so that I got used to it.

To be honest using the C approach

But it's *not* the C approach! Using the set of features shared
between C and C++ is perfectly fine C++, as long as you're not doing
it to avoid some much better C++-only feature (e.g. malloc+arrays
versus std::vector).
is easier, faster (in terms of
development time) and in my opinion clearer but since this code will be
used by others who perhaps are not used to the C way of doing things I
was a little concerned that it might put them off.

If you just write C in C++, it /will/ put "them" off if they are C++
programmers. Otherwise, see above.

/Jorgen
 
J

Jorgen Grahn

.
To me it's a question of the reasonable expectations of the programmer
using the class. In socket/file io experienced programmers know what is
under the hood of the class and expect to be able to test/catch errors.
My preference is for testing in those cases because of efficiency
concerns.

I doubt using exceptions for real I/O errors would hurt performance.

But it would be ugly unless the rest of your object design is just
right -- you'd probably catch close to the throw site, inside tight
loops. At least that's how it seems to me: I've failed to do such
things myself, and I haven't looked at any other Socket API wrappers.

/Jorgen
 
G

Greg Martin

I doubt using exceptions for real I/O errors would hurt performance.

But it would be ugly unless the rest of your object design is just
right -- you'd probably catch close to the throw site, inside tight
loops. At least that's how it seems to me: I've failed to do such
things myself, and I haven't looked at any other Socket API wrappers.

/Jorgen

I presumed that unwinding the stack vs simply returning a value would
take more cycles but you may be right and in some circumstances it
wouldn't matter but I agree that it could be an ugly way to manage
something that wouldn't be improved by it anyway.
 
W

W Karas

I've seen various arguments against this primarily centring on the fact

that the only way to return errors in a constructor is to throw an

exception. Of course exceptions seem to be a somewhat controversial

subject in the C++ community so I'll try and avoid touching on that.



But I have a couple of classes that are created when the program

launches and are freed just before the program terminates. It makes

sense for me to put a lot of logic in the constructor as these classes

are initialised from data in different configuration files which are

passed as command line arguments.



Is there any reason I shouldn't put all the file loading / reading /

storing of data in the constructor? Or would you not consider this a

problem? For reference I'm using libxml2 for reading the data in the

configuration files.

Let me try to flank this rather than a frontal assault.

A well-designed object has a well-known set of states. It can get very tricky to meet this requirement in the face of run-time errors. There are nuclear options like reseting the processor or calling std::exit(). Exceptions provide a less apocalyptic alternative. Without aborting or exceptions, at least one "invalid" state (meaning "don't do anything to me other than destroying me") may be unavoidable. I'd suggest a rule of thumb that, in such cases, the object class have a public const member function that will report when the object is in an invalid state. This greatly reduces the importance of member functions being able to return a value indicating an error, and thus the problem of errors during construction.
 
D

David Sawyer

I have put whole programs in constructors, like this:

#include "monsterobject.h"

int main()
{
monster_object m;
return 0;
}
 
8

88888 Dihedral

在 2013å¹´1月27日星期日UTC+8上åˆ7æ—¶21分40秒,Johnson写é“:
Here is my question: I have a sorted list that includes hundreds of

decimal values, from minimum to maximum. Now I have a decimal input,

and I want to get the closest value existing in the list to match this

input. Is there any way to accomplish this task in C++ with a faster

speed than the binary search?

Thank you!



--- news://freenews.netfront.net/ - complaints: (e-mail address removed) ---

I'll sugest to use the GMP library.
http://www.shoup.net/ntl/doc/tour-gmp.html
 
8

88888 Dihedral

在 2013å¹´1月27日星期日UTC+8上åˆ11æ—¶26分14秒,88888 Dihedral写é“:
在 2013å¹´1月27日星期日UTC+8上åˆ7æ—¶21分40秒,Johnson写é“:




I'll sugest to use the GMP library.

http://www.shoup.net/ntl/doc/tour-gmp.html
I mean in building a hash table with a key bitsize
128 bits or aboveto ensure the O(1) search
efficience for billions of items in a
true 64 bit OS.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,062
Latest member
OrderKetozenseACV

Latest Threads

Top