'Should constructor call init or vice versa?

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.

c++


Solution 1:[1]

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

Solution 2:[2]

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andrea Sesto
Solution 2 Soonts