One-Step vs Two-Step Initialization (C++)

Started by
32 comments, last by Matias Goldberg 11 years, 9 months ago
I prefer one step initialization. Unless you have a thing for extra typing when creating objects. Also, I find that one-step initialization tends to make you think more about object graph / dependencies, and so you are less likely to end up with co-dependent / mutually dependent objects and order-of-initialization problems.

The issue of how to handle initialization failure (exception or flag or something else) is completely independent of whether or not you do one/two step init. A constructor can flag invalid? or throw and so can ::initialize(). Error handling is important but for this particular topic it is a red herring issue.

The only practical consideration here that I can think of (besides extra typing) is whether or not you want to be able to put your object (and not a pointer to it) into STL or similar containers. Many/most containers require that your object have a no-argument constructor. If a no-arg constructor makes sense and you want to put the object into a container, then one-step is much more practical.

If you aren't sure, write a separate ::initialize method and have your ctor call it. Be sure to do the right thing if ::initialize() is called twice. If the object in question lends itself to object pooling, then you'll want ::initialize(), ::reinitialize(), ::clear() or similar so that you can tidy it up before you put it back in the pool. Speaking of pooling (I realize this is going a bit off topic), if you think you are likely to end up with pooling then make all your ctor's private and use a static class method to get an object instance.
Advertisement
I use one-step almost exclusively, the code is simply cleaner/shorter that way. I do very rarely use two-step, almost always because I wish to reuse memory(say the type in question contains a few dynamic buffers), but even here I hide the fact that it is two step initialization from the user of the object(template magic).

You could easily do the same thing with one-step by using a factory.
@Matias Goldberg: I got that. I didn't want to imply you recommended copy&paste programming. When you wrote "...rather than having to refactor everything or resort to copy-pasting the constructor into a reinit() function, then debug why reinit is failing (because you pasted more code than you should, or forgot to copy something).", in my opinion [font=courier new,courier,monospace]reinit()[/font] is a) just another form of two-stage initialization and b) can avoid code duplication just the same (it's not like [font=courier new,courier,monospace]reinit()[/font] would somehow force it).

I would still strongly prefer one-stage initialization in all 4 cases you listed. But instead of just criticizing your design, let me explain how I would have done it and you can criticize mine tongue.png)

Object reloading for memory & performance: I have a logical game object which maintains the persistent state (eg. [font=courier new,courier,monospace]class ScarySpider[/font] with [font=courier new,courier,monospace]int health[/font], [font=courier new,courier,monospace]Item loot[/font], [font=courier new,courier,monospace]int xp[/font]). Add AI, physics etc. via composition. This thing gets loaded and saved with a level and in savegames. With a client/server model, the server would only work with this logical game object. To actually render it, there is a [font=courier new,courier,monospace]class ScarySpiderPresenter[/font] which creates and maintains the visual and audible representation of the logical game object in the game's scene graph. If I wanted to reclaim memory this way, I'd destroy the [font=courier new,courier,monospace]ScarySpiderPresenter[/font] but leave the [font=courier new,courier,monospace]ScarySpider[/font].

Reloading for in-place editing: Kill [font=courier new,courier,monospace]ScarySpiderPresenter[/font], create [font=courier new,courier,monospace]ScarySpiderEditorPresenter[/font]. The latter could derive from the former or both could derive from a common base class as appropriate if there is shared functionality. This presenter could draw bounding boxes, overlay the AI's current patrol path / attack target or display scale and rotate widgets or whatever you like. Normal gameplay is not burdened with dragging the editor-specific state variables along as dead code.

Level reloading: Either be very clever and mutate the active world's state into the state loaded from the saved game / level (this is how I understood your approach) which will neatly cause the presenter hierarchy to create/destroy presenters as needed, all without requiring the save/load code to get clogged dealing out references to graphics devices, input managers and audio managers to the newly created objects. Or alternatively go ahead and kill the world with its presenters, then recreate it, taking into account that long loading times primarily result from needlessly reloading resources from disk - which can be solved by a resource manager with a LRU list and a memory budget.

So in short, whenever two-stage initialization is used, there are actually two classes being glued together. Yes, design gets more complicated as a project's scope grows and the effort to change late in the process becomes ever larger. All the more reason to pay attention to it, especially if one is not just coding Tetris of Pac Man!
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.

@Matias Goldberg: I got that. I didn't want to imply you recommended copy&paste programming. When you wrote "...rather than having to refactor everything or resort to copy-pasting the constructor into a reinit() function, then debug why reinit is failing (because you pasted more code than you should, or forgot to copy something).", in my opinion [font=courier new,courier,monospace]reinit()[/font] is a) just another form of two-stage initialization and b) can avoid code duplication just the same (it's not like [font=courier new,courier,monospace]reinit()[/font] would somehow force it).

Oh I see the confusion. Let me rephrase and be more clear what I was trying to say:
When using the one-step, eventually something may pop up that requires a reinitialization that is inconsistent or hard to solve with the one-step initialization solution you've designed from the beginning. To solve that issue, you have three choices:

  1. Refactor everything so that you still end up using One-step init. The most elegant solution but requires time to refactor.
  2. Modify the affected part so it ends up as a two-step init. (for this/these particular case(s)). For example, first pass may contain immutable data, while second one would contain data that is supposed to be reinitialized. When it's time to reinit, just call the second pass' function. Much faster to code and still elegant.
  3. Copy / paste the constructor into a different function and use that function when you have to reinit as an exceptional case--> Avoid this, it's error prone.



I would still strongly prefer one-stage initialization in all 4 cases you listed. But instead of just criticizing your design, let me explain how I would have done it and you can criticize mine tongue.png)

Cool


Object reloading for memory & performance: I have a logical game object which maintains the persistent state (eg. [font=courier new,courier,monospace]class ScarySpider[/font] with [font=courier new,courier,monospace]int health[/font], [font=courier new,courier,monospace]Item loot[/font], [font=courier new,courier,monospace]int xp[/font]). Add AI, physics etc. via composition. This thing gets loaded and saved with a level and in savegames. With a client/server model, the server would only work with this logical game object. To actually render it, there is a [font=courier new,courier,monospace]class ScarySpiderPresenter[/font] which creates and maintains the visual and audible representation of the logical game object in the game's scene graph. If I wanted to reclaim memory this way, I'd destroy the [font=courier new,courier,monospace]ScarySpiderPresenter[/font] but leave the [font=courier new,courier,monospace]ScarySpider[/font].

It's pretty much how my system already works ;)
There will be a few (solvable) issues I ran to, for example the AI needs bounding box information for their calculations, which needs to be handled correctly during the composition or else they'll end up tied together. The Physics need to access the Animation data to extract motion, and so on..
Unfortunately in my case destroying ScarySpiderPresenter is hard (but not impossible) because the Graphics is manipulated from a different thread than Logic. However, I do have control free of what happens inside ScarySpiderPresenter though; but since I use Ogre I start being tied to what happens inside Ogre::Entity.
A trade off from using an existing tech rather than rolling my own or modifiying existing one. Unfortunately Ogre is not very multi-thread friendly and I don't have time to spend on it, but I've already submitted a proposal for 2.0. It would be very easy for me to implement those features but it takes time, and currently I'm commited into finishing my own project.


Reloading for in-place editing: Kill ScarySpiderPresenter, create ScarySpiderEditorPresenter. The latter could derive from the former or both could derive from a common base class as appropriate if there is shared functionality. This presenter could draw bounding boxes, overlay the AI's current patrol path / attack target or display scale and rotate widgets or whatever you like. Normal gameplay is not burdened with dragging the editor-specific state variables along as dead code.

That's a very interesting method (and surprisingly simple, I like it). I'll take it into account! Thanks.

Level reloading seems fine, there are hundreds of ways of doing it. Like I said that's the one that gets often most attention.

This topic is closed to new replies.

Advertisement