• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
blueskies9041

C++ Constructors vs. Init() Functions

44 posts in this topic

I'm learning game programming at AIE US - I've reviewed a lot of source code while developing my projects, 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? I know constructors can use initialization lists which has a few benefits that I don't fully understand yet .

 

Thanks :]

0

Share this post


Link to post
Share on other sites

The difference between them (obvious example inc!):

class MyClassCons
{
    public:
        MyClassCons()
        {
            Pointer* p = new Pointer();
        }
};

class MyClassInit
{
    public:
        MyClassInit();
        void Init()
        {
            Pointer* p = new Pointer();
        }
};

int main()
{
    MyClassConsc aClass;
    //p* already exists!

    MyClassInit bClass;
    //p* doesn't exist yet!
};

The problem with this is that sometimes p* could be a variable that depends on something ELSE to work.

Using a really bad example, say you put some OpenGL commands in your class' default constructor. Just by definining the class somewhere you'll get a runtime error because when the variable was created, right at the start of your program, the OpenGL context which is required before issuing glCommands wasn't active/didn't exist.

 

But you do could use Init(); since it would ONLY be called when you explicitly say so:

int main()
{
    MyClassInit aClass; //it's okay

    InitOpenGL();
    aClass.Init(); //okay, no runtime error
}

But you couldn't do the otherway around:

int main()
{
    InitOpenGL(); 

    MyClassCons aClass; //Still runtime error
}

Other than this situation (which I found to be sorta common), I think everything else is due preference...

2

Share this post


Link to post
Share on other sites


The problem with this is that sometimes p* could be a variable that depends on something ELSE to work.

 

Especially if you have static objects form "MyClass". You cant really solve that by creating the objects in the right order.

0

Share this post


Link to post
Share on other sites


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.)

0

Share this post


Link to post
Share on other sites
A few other remarks on this choice:
- if you define your own constructor, the 'default' constructor, destructor and assignment operator are no longer applied from the compiler, meaning you need to write them yourself to (look up "the rule of three")
- when deciding this you should also think ahead if you want objects of the class to be reinitialized/ reused later and how you'll handle this (doing a own init function might need a cleanup partner :))
-3

Share this post


Link to post
Share on other sites

In C++, constructors are really important for RAII. Note that they are required if you wish to follow the objects-are-never-in-an-invalid-state thought process.

 

Personally, if I have to have confirmation that "initialization was a success", I'll add an IsInit or IsValid function. These cases are very, very rare in real life situations. Also... once you start using smart pointers it will be silly to explicitly Init after creating the object...

2

Share this post


Link to post
Share on other sites

One setup I see often is having a private constructor and then some sort of static create function that returns a pointer to the class.

0

Share this post


Link to post
Share on other sites

- if you define your own constructor, the 'default' constructor, destructor and assignment operator are no longer applied from the compiler, meaning you need to write them yourself to (look up "the rule of three")

 

Unless somebody made really weird changes to the language, that's simply not true. Creating your own constructor will never prevent default implementations of anything but the default constructor.

 

The "Rule of Three " doesn't say "you must implement all of them, because the compiler doesn't create them", it says "if you need one, you probably need all three". In fact, the big problem IS that the default implementations don't do everything they should in that case.

 

Obvious example:

 


class Demo()

{

    Demo() : ptr(new Thing) {}

    ~Demo() { delete ptr; }

 

    Thing* ptr;

}

 

And you probably don't want to know how often I'm seeing this kind of thing, always hand-waved away with "nah, it's never going to be copied anyway". Unless it suddenly is and "someone" (typically me) gets to spend hours debugging and tracking down the double delete introduced months ago.

 

If the compiler actually would stop creating defaults for assignment and _copy_ constructor (which is the one relevant to the Rule of Three) the code would have the decency to stop compiling. You'd also have an army of C++ coders lynching the person that came up with it.

 

 

Another common rule is that the constructor should only do minimal work to get the object in a valid state. Any heavy lifting that isn't absolutely needed would then go into an init() function.

Edited by Trienco
2

Share this post


Link to post
Share on other sites

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).

Edited by slayemin
0

Share this post


Link to post
Share on other sites
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.
0

Share this post


Link to post
Share on other sites


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?

2

Share this post


Link to post
Share on other sites

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.
0

Share this post


Link to post
Share on other sites

 


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.

0

Share this post


Link to post
Share on other sites

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.

0

Share this post


Link to post
Share on other sites

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
       }
    }
0

Share this post


Link to post
Share on other sites

 


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?

0

Share this post


Link to post
Share on other sites

 

 


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?

 

Yes, throwing an exception is the standard way of handling a constructor that fails.

http://www.parashift.com/c++-faq-lite/ctors-can-throw.html

 

I feel offloading work into an init() method is somewhat "hackish" and goes against the RAII principle, not to mention everyone who uses your class has to remember to call init().

0

Share this post


Link to post
Share on other sites

I feel offloading work into an init() method is somewhat "hackish" and goes against the RAII principle, not to mention everyone who uses your class has to remember to call init().

If you come from a background of older languages, such as C, pre-90's BASIC, cobol, pascal, ada, or even machine code, RAII means something very different.

 

In the older languages, allocation means you get a blob of memory with completely unspecified data inside. It might be zeros. It might be white noise. It might be the contents from a previous (perfectly valid) object. The next step after allocating is nearly always to initialize the data to known values. It could be bzero() or memset() to initialize the values to zero. It could be to initialize them to otherwise known values. Or sometimes, people would follow the less-than-ideal practice of leaving the memory buffer around with garbage data until they get around to putting real data in.

 

Most modern languages (under two decades old) have strict enforcement of RAII. For example, in Java or C# if you write int i; the compiler automatically applies RAII and initializes it to a value of zero on your behalf. If you write bool b; the language performs RAII and gives you a value of false. 

 

In contrast with older languages (from two decades to nearly seven decades old) in the past when you created an integer it contained whatever happened to be in a register or memory address at the time. It might be zero, 36, 72, or any other value. Unless you initialized it you had no guarantees about the contents until you performed a separate initialization step.

 

 

So really, the RAII cries you hear are mostly an old vs. young interpretation.  Those who have only worked in young languages (Java is still a teenager) and not studied history often interpret RAII as requiring multiple resource allocations and performing costly work. Those who have worked in older languages or who have studied history interpret RAII as just doing something as simple as bzero() after a successful malloc().

1

Share this post


Link to post
Share on other sites
Wouldn't throwning an exception inside a constructor cause a memory leak, as the destructor would never get called?

This seems hackish to me.

What about occasions where the error is 'not the end of the world' and the fallback option might not even have anything to do with that class (i.e. you don't need that class if the init() fails).

Seems funny to throw an exception from the constructor in that case. I'd still go for an init() function. Edited by lonewolff
0

Share this post


Link to post
Share on other sites

 

- if you define your own constructor, the 'default' constructor, destructor and assignment operator are no longer applied from the compiler, meaning you need to write them yourself to (look up "the rule of three")

 

Unless somebody made really weird changes to the language, that's simply not true. Creating your own constructor will never prevent default implementations of anything but the default constructor.

 

The "Rule of Three " doesn't say "you must implement all of them, because the compiler doesn't create them", it says "if you need one, you probably need all three". In fact, the big problem IS that the default implementations don't do everything they should in that case.

 

Obvious example:

 


class Demo()

{

    Demo() : ptr(new Thing) {}

    ~Demo() { delete ptr; }

 

    Thing* ptr;

}

 

And you probably don't want to know how often I'm seeing this kind of thing, always hand-waved away with "nah, it's never going to be copied anyway". Unless it suddenly is and "someone" (typically me) gets to spend hours debugging and tracking down the double delete introduced months ago.

 

If the compiler actually would stop creating defaults for assignment and _copy_ constructor (which is the one relevant to the Rule of Three) the code would have the decency to stop compiling. You'd also have an army of C++ coders lynching the person that came up with it.

 

 

Another common rule is that the constructor should only do minimal work to get the object in a valid state. Any heavy lifting that isn't absolutely needed would then go into an init() function.

 

 

Thanks, I've misunderstood it and looked it up:

 

 

The rule of three (also known as the Law of The Big Three or The Big Three) is a rule of thumb in C++ that claims that if a class defines one of the following it should probably explicitly define all three:[1]

These three functions are special member functions. If one of these functions is used without first being declared by the programmer it will be implicitly implemented by the compiler with the default semantics of performing the said operation on all the members of the class. The default semantics are:

  • Destructor - Call the destructors of all the object's class-type members
  • Copy constructor - Construct all the object's members from the corresponding members of the copy constructor's argument, calling the copy constructors of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members
  • Copy assignment operator - Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g., int or pointer) data members.

The Rule of Three claims that if one of these had to be defined by the programmer, it means that the compiler-generated version does not fit the needs of the class in one case and it will probably not fit in the other cases either.

1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0