Re: Should you perform complex tasks in the constructor?
On 13-01-11 04:42 AM, Chicken McNuggets wrote:
On 10/01/13 22:21, Greg Martin wrote:
On 13-01-10 01:54 PM, Chicken McNuggets wrote:
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
}
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 ();