14

I still consider myself as an apprentice programmer, so I'm always looking to learn a "better" way for typical programming. Today, my coworker has argued that my coding style does some unnecessary work, and I want to hear opinions from others. Typically, when I design a class in OOP language (Usually C++ or Python), I would separate the initialization into two different parts:

class MyClass1 {
public:
    Myclass1(type1 arg1, type2 arg2, type3 arg3);
    initMyClass1();
private:
    type1 param1;
    type2 param2;
    type3 param3;
    type4 anotherParam1;
};

// Only the direct assignments from the input arguments are done in the constructor
MyClass1::myClass1(type1 arg1, type2 arg2, type3 arg3)
    : param1(arg1)
    , param2(arg2)
    , param3(arg3)
    {}

// Any other procedure is done in a separate initialization function 
MyClass1::initMyClass1() {
    // Validate input arguments before calculations
    if (checkInputs()) {
    // Do some calculations here to figure out the value of anotherParam1
        anotherParam1 = someCalculation();
    } else {
        printf("Something went wrong!\n");
        ASSERT(FALSE)
    }
}

(or, python equivalent)

class MyClass1:

    def __init__(self, arg1, arg2, arg3):
        self.arg1 = arg1
        self.arg2 = arg2
        self.arg3 = arg3
        #optional
        self.anotherParam1 = None

    def initMyClass1():
        if checkInputs():
            anotherParam1 = someCalculation()
        else:
            raise "Something went wrong!"

What is your opinion about this approach? Should I refrain from splitting the initialization process? The question is not only limited to C++ and Python, and answers for other languages are also appreciated.

28

Though sometimes it is problematical, there are many advantages to initializing everything in the constructor:

  1. If there is going yo be an error, it happens as quickly as possible and is easiest to diagnose. For example, if null is an invalid argument value, test and fail in the constructor.
  2. The object is always in a valid state. A coworker can't make a mistake and forget to call initMyClass1() because it isn't there. "The cheapest, fastest, and most reliable components are those that aren't there."
  3. If it makes sense, the object can be made immutable which has lots of advantages.
2

Think about the abstraction you're providing your users.

Why split something that could be done in one shot into two?

The extra initialization is just something extra for programmers using your API to remember, and provides more to go wrong if they don't do it right, but for what value to them for this extra burden?

You want to provide dead simple, easy to use, hard to go wrong abstractions. Programming is hard enough without gratuitous things to remember / hoops to jump through. You want your API users (even if it is just you using your own API) to fall into the pit of success.

1

Initialize everything except big data area. Static analysis tools will flag fields not initialized in constructor. However, the most productive/safe way is to have all member variables with default constructors and explicitly initialize only ones that require non-default initialization.

0

There are cases where the object has a lot of initialisation which can be divided into two categories:

  1. Attributes that are either immutable or do not need to be reset.

  2. Attributes that might need to revert to original values (or templatised values) based on some condition after fulfilling their job, kind of a soft reset. e.g. connections in a connection-pool.

Here the second part of the initialisation being kept in a separate function, say InitialiseObject(), can be called in the ctor.

The same function can be called later if a soft reset is required, without having to discard and recreate the object.

0

As others have said, it's generally a good idea to initialise in the constructor.

There are, however, reasons not to that may or may not apply in specific cases.

Error handling

In a lot of languages, the only way to signal an error in a constructor is to raise an exception.

If your initialisation has a reasonable chance of raising an error e.g. it involves IO or its parameters could be user input, then the only mechanism open to you is to raise an exception. In some cases, this may not be what you want and it may make more sense to separate out the error prone code to a separate initialisation function.

Probably the most common example of this is in C++ if the project/ organisational standard is to switch exceptions off.

State machine

This is the case where you are modelling a object that has explicit state transitions. For example, a file or a socket that can be opened and closed.

In this case, it is common for the object construction (and deletion) to only deal with memory oriented attributes (file name, port etc). There will then be functions to specifically manage the state transitions e.g. open, close which are effectively initialisation and tear down functions.

The advantages are in error handling, as above but also there may be a case for separating the construction from initialisation (say you build a vector of files and open them asynchronously).

The disadvantage, as others have said, is that you now put the burden of state management on the user of your classes. If you could manage with just construction then you could, say, use RAII to do this automagically.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.