C++ Constructors vs. Init() Functions

Started by
43 comments, last by nobodynews 10 years, 1 month ago

I don't know if its the same in C++ as it is in C#, but I hit this problem today: If your class is inheriting from a base class, your default constructor will invoke the base class default constructor first. If your base class is depending on a specific variable to be allocated by the inheriting class object, you're going to get a null reference error in the base class.


-> create new ClassObject
	-> Set any variables to preset constant values
	-> ClassObject.CTOR(Settings)
		-> Set any base class variables to preset constant values
		-> base.CTOR() code run
	<- ClassObject.CTOR code run
<- ClassObject reference returned to caller

Where is the problem?

If any of the base.CTOR() fields need to be initialized with the settings values passed into the ClassObject, you are out of luck because the class constructor code is evaluated AFTER the base class code is run. You can not change the calling order of constructors.

However, if you include an "Initialize(Settings)" method, you can specify the call order:

1. -> create new ClassObject
	-> Set any variables to preset constant values
	-> ClassObject.CTOR()
		-> Set any base variables to preset constant values
		-> empty base CTOR run
	<- empty class object CTOR code run
   <- ClassObject reference returned to caller
	
2. -> ClassObject.Initialize(Settings)
	-> Set any object memory/variables
	-> base.initialize(Settings)
		->Set any base level memory/variables

3. -> ClassObject.LoadContent()
	-> Run any code necessary to pull external assets from disk into main memory 

I decided that I could consolidate all of my class initialization code into a constructor. As I found out, this was a bad design decision.

As Frob suggests above, you want to be able to create "empty" objects.

Here are the principles I try to follow:
-I should be able to create a new object which sets the object properties to initial values. The object is empty and ready to be initialized.

-When an object is initialized with settings, memory is allocated and variables are set.

-I should be able to call a "LoadContent()" method which pulls any external assets from disk into main memory. I should be able to call this as many times as I want, but content is pulled from disk only once.

-I should be able to call an "UnloadContent()" method which releases the external assets from main memory. The object state is returned to the same state it was in before LoadContent() was called.

-To prove this, I should be able to call LoadContent() -> UnloadContent() -> LoadContent() as many times as I want without breaking the objects usability.

-Optional: You can use lazy initialization if you keep a boolean flag to keep track of whether or not object content is loaded. The game tries to use the object, realizes the external assets aren't loaded, goes to load the assets, and then resumes usage of the object (I think the RIFT MMORPG uses this technique).

Advertisement
As BeerNutts pointed out - 'return codes'.

If you have initialisation code in your constructor that requires some sort of error handling it is harder to determine if the call failed.


If your base class is depending on a specific variable to be allocated by the inheriting class object, you're going to get a null reference error in the base class.

This seems to be a strange design. Can you give a more concrete example? Why would the base class know about variables of the derived class?

I've noticed that some people use either a constructor or a plan old void (or bool) Init() function to initialize their object's member variables.

Is there a good reason to use one or the other?

Some people know C better than they know C++, so they use C idioms instead of using C++ properly tongue.png

As BeerNutts pointed out - 'return codes'.

If you have initialisation code in your constructor that requires some sort of error handling it is harder to determine if the call failed.

1) Out params. e.g. new Foo(1,2,3, &error);
2) Exceptions.
3) Factory methods e.g. Foo::New(1,2,3) may return NULL.
4) This usually isn't even a concern -- most objects can't fail initialization.

If your base class is depending on a specific variable to be allocated by the inheriting class object, you're going to get a null reference error in the base class.

This seems to be a strange design. Can you give a more concrete example? Why would the base class know about variables of the derived class?

Indeed. This is either an abuse of inheritance for the purposes of extension/code-reuse (that's the job of composition), or it's solved by:


//old
class Base { Base() { use buffer here, its uninitialized, crash! } int* buffer; }
class Derived { Derived() { buffer = new int; } } -- Derived initializes bases members. Thats ugly.

//fixed
class Base { Base( int* buffer, int size ) : buffer(buffer) { use of buffer here is fine } int* buffer; }
class Derived { Derived() : Base( new int[2], 2 ); }

I've noticed that some people use either a constructor or a plan old void (or bool) Init() function to initialize their object's member variables.
Is there a good reason to use one or the other?

Some people know C better than they know C++, so they use C idioms instead of using C++ properly :P

As BeerNutts pointed out - 'return codes'.
If you have initialisation code in your constructor that requires some sort of error handling it is harder to determine if the call failed.

1) Out params. e.g. new Foo(1,2,3, &error);
2) Exceptions.
3) Factory methods e.g. Foo::New(1,2,3) may return NULL.
4) This usually isn't even a concern -- most objects can't fail initialization.

I was thinking more of the DirectX point of view. In my case, I have a lot of DX initialisation in my class and, as we know, there is plenty that can go wrong there.


If your base class is depending on a specific variable to be allocated by the inheriting class object, you're going to get a null reference error in the base class.

This seems to be a strange design. Can you give a more concrete example? Why would the base class know about variables of the derived class?

Yeah, I kind of thought so to. Here is an abbreviated code sample:


public class AssetDB
{
   ContentManager m_content;
   public AssetDB(ContentManager content)
   {
       m_content = content;
   }
}

public class WorldSettings
{
   public ContentManager m_content;
}

public abstract class GenericGameWorld
{
   AssetDB m_assetDB;
   WorldSettings m_settings;

   public GenericGameWorld()
   {
       m_assetDB = new AssetDB(m_settings.m_content);  //<-- not set
   }
}

public class TacticalGameWorld : GenericGameWorld
{
   public TacticalWorld(WorldSettings settings)
   {
       m_settings = settings;   //base constructor called before this runs
   }
}

public class GameMain : Game
{
    TacticalWorld m_tacticalWorld;
    WorldSettings m_settings;

    public GameMain()
    {
        m_settings.m_content = this.Content;
        m_tacticalWorld = new TacticalWorld(m_settings);
    }
}

In hindsight, I *could* just new the assetDB in the classes which implement the GenericGameWorld class, but logically it seems that if a class owns a field, then it should also manage it so that there is loose coupling between classes.

I was thinking more of the DirectX point of view. In my case, I have a lot of DX initialisation in my class and, as we know, there is plenty that can go wrong there.

The vast majority of D3D errors are usage errors from invalid code, rather than actual problems to be expected normally. These kinds of errors should be fed into an assert statement that crashes in debug/development builds and omitted completely in tested/shipping builds. ;)

One reason why an Init could be better is that you can safely call virtual functions here, whereas that is not possibly in the constructor/destructor. I had a issue just a few days ago where for the first time I kind of regreted using constructors:


class BaseEffect : IEffect
{
public:
            BaseEffect(ApiEffect& effect): m_pEffect
            {
                  // OnChangeEffect should be called here, but can't
            }

            ~BaseEffect()
            {
                   // OnDeleteEffect should be called here but once again, can't.
            }

            virtual void OnChangeEffect(ApiEffect&effect) = 0;
            virtual void OnDeleteEffect(ApiEffect& effect) = 0;

private:

           ApiEffect* m_pEffect;
}

class DX9Effect final: BaseEffect
{ 
             DX9Effect(ApiEffect& effect) : BaseEffect(effect)
             {
                   // due to this, I have to call OnChangeEffect explicitely in each implementation here,
                   // which is prone to error and produces dublicated code
                   OnChangeEffect(effect);
             }

             ~DX9Effect()
             {
                   // same here. I even have to speificially ad a getter for m_pEffect so that I can delete it here
                   OnDeleteEffect(EffectUnsafeGetter());
             }

             void OnChangeEffect(ApiEffect& effect)  override
             {
                      m_states.Add<BindShader>(effect.GetShader());
             }

             void OnDeleteEffect(ApiEffect& effect) override
             {
                       delete &effect;
             }
}

Since ApiEffect is a typedef that is only forward-declared at "BaseEffect", I have to have a method for implementations to specifiy deletion, and also each effect derived class needs specific behaviour on initialization, but unfortunately this cannot be realized via ctor/dtor, since the vtable is not fully utilized at this point, forcing me to write some unnecessary code for every implementation. One reason why you could prefer "Init/Deinit" in a specific situation.

In hindsight, I *could* just new the assetDB in the classes which implement the GenericGameWorld class, but logically it seems that if a class owns a field, then it should also manage it so that there is loose coupling between classes.


What about this:


    public abstract class GenericGameWorld
    {
       AssetDB m_assetDB;

       protected GenericGameWorld(WorldSettings settings)
       {
           m_assetDB = new AssetDB(settings.m_content);
       }
    }

    public class TacticalGameWorld : GenericGameWorld
    {
        WorldSettings m_settings;

        public TacticalGameWorld(WorldSettings settings)
            : base(settings)
       {
           m_settings = settings;   //base constructor called before this runs
       }
    }


Well, constructor's don't have any kind of return value, so, many people use an Init() type function that returns an error code of some sort.

The standard way would be to throw an exception from the constructor. (I dont do that either.)

I would never do anything "complicated" inside the constructor, like loading images, setting up textures, etc. I would just initialise member variables to a known state and leave the other stuff for an init function/method or using getters/setters, depending on the situation. I wouldn't do anything that could throw exceptions inside a constructor. Throwing an exception inside a constructor sounds like a bad thing to me. Is this really the standard way?

This topic is closed to new replies.

Advertisement