Home > Blockchain >  Should constructor call init or vice versa?
Should constructor call init or vice versa?

Time:02-14

In a case where I want to allow initializing a class directly in the construction, as well as allowing an empty instance (either default-constructed or that has had some kind of close() method called on it) to be initialized, is there any reason to prefer either of these two options for avoiding code duplication?

init/open calls constructor:

struct S {
  S(params...) : initlist... {
    ... init code ...
  }
  void init(params...) {
    *this = S(params...);
  }
  ...
};

Constructor calls init/open:

struct S {
  S(params...) {
    init(params...);
  }
  void init(params...) {
    ... init code ...
  }
  ...
};

Think for example a class representing a file, that can have the path passed to the constructor or call an open() method later.

CodePudding user response:

I would argue neither. Except in rare instances where you want a two step construction process, I'd argue against having an "init" method at all.

Your first option "init calls constructor" actually uses a copy or move constructor for the initializing process. But it hides the fact that is does so. Why?

Wouldn't this be better, instead of introducing another method:

   S obj;   // create default instance
   obj = S(params...);   // init instance with copy/move constructor

// or after a close()
   obj.close();
   obj = S(params...);

Your second option is something seen a lot in older C code, pre C 14 (11 perhaps), where delegating constructors had not been created. Usually with classes that had more than one constructor.

   S(int one, param two, special three)
   {
      init(one, two);
      // and do something with three
      someThree = three;
   }
   s()
   {
      init(10, param::none);
   }

Here init would be used for some basic, common initialization and specific initialization could be added after that.

But with delegating constructors, init could easily be replaced by a specific 'base' constructor and the other constructors would simply use it.

   S(int one, param two)
   {
      myOne = one; ... // other default 'init' stuf
   }
   S(int one, param two, special three)
      :S(one, two)   // call "init", delegate to the "init" constructor
   {
      // and do something with three
      someThree = three;
   }

}

My argument here is that init is a constructor and should be written as such and not as "init". Or, to put it the other way round, once you have constructed an object, there is no need for another "init".

  • A constructor should fully initialize an object.
  • Anything done to the object after that is not initialization.

You could have a reset, if you want specific parameters to be reset. But that's not really an "init".

CodePudding user response:

You might use different constructors: one with no parameters (that sets empty/default values) and one with the parameters you need. You can implement the Init method as a standalone private method called by the second constructor, or as body of the second constructor, depending on your taste.

struct S {
  /* Default / empty constructor */
  S() {
    // Code for the default / empty constructor
  }

  /* Parameters constructor */
  S(param1, param2, ...)
    // Init code here or call to Init function
  }
  
  // Init method code, if separated from constructor
};

This is quite clean, as a programmer will search for a constructor by default, and not for any other initialization method. If the code of the 2 constructors is not conflicting, and if you're using C 11 as i guess, you could also do something like:

struct S {
  /* Default / empty constructor */
  S() {
    // Code for the default / empty constructor
  }

  /* Parameters constructor */
  S(param1, param2, ...): S() // calls empty constructor first
    // Init code here or call to Init function
  }
  
  // Init method code, if separated from constructor
};

where the second contructor calls the fist one before executing its init procedures

CodePudding user response:

I usually using just the init method because I tend to avoid exceptions, and constructors can't return status codes.

If you also want a non-default constructor, I think your second example is better. Your first version requires the class to be copyable or moveable. Depending on what's in your class, that requirement may complicate things. For instance, it's unnecessarily complicated to implement copies of file handles, you gonna need kernel calls like DuplicateHandle.

  •  Tags:  
  • c
  • Related