Constructor gotcha's?

Started by
25 comments, last by Norman Barrows 9 years, 2 months ago

Recently i've been thinking about what form an oo-ish implementation of my typical game architecture would look like. so far, the mapping from procedural to oo code has been amazingly lucid, with some interesting organizational insights gained along the way such as camera.cansee(location) where location includes a bbox or bsphere radius, or AABrect dimensions. in the procedural code there are inits (and some uninits) for many things such as: program, game, generic_graphics_engine, generic_audio_engine, asset pools, etc. naturally, these are prime candidates for custom constructors and destructors.

i'm aware that statically declared objects are initialized in an order not under your direct control. other than that, are there any "gotcha!"s to look out for? IE issues - things you don't want to do for some reason in a custom constructor or destructor?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

Advertisement
Depends. What language?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

i'm aware that statically declared objects are initialized in an order not under your direct control. other than that, are there any "gotcha!"s to look out for? IE issues - things you don't want to do for some reason in a custom constructor or destructor?

I've had several of annoyances with static variables. Not just initialization order, but also weird issues when trying to use them across library boundaries (if you want to seperate your engine into a library).

Also, it's not just the initialization order of your classes, but also the initialization order of your classes relative to the APIs you might be using. As an example, accidentally loading assets prior to the OpenGL context being created, or trying to output an error message prior to your logger class being initialized. dry.png

To enforce an initialization order, you can put them all in a struct - for me, my 'GameStructure' class holds the asset pools and graphics engine and etc... 'GameStructure' being bare-bones owner (composition-wise) of everything except the globals (and I've been removing more and more of the globals, so there's only two or three left).

To enforce a specific initialization time, I explicitly construct the GameStructure after control is actually handed to my program.

I hate that you can't take a pointer to constructors and destructors. Also a lot of the time there's a huge separation between allocation and initialization, though constructors attempt to package this into a single piece of code. Lastly C++ sucks when you define a constructor and yet want to create an array of your object on the stack and you get the error saying that's not possible without a trivial default constructor.

It seems like the moment you start really using constructors and destructors you have to subscribe to a bunch of other OO nonsense. At least, this is my own opinion.

Edit: Whoops lots of downvotes. I'll try to be more clear next time! I agree with all the corrections everyone made below. It's not that I don't understand how C++ works or why it is the way it is, I just disagree with it. Please take a look at the below posts responding to this one.


Depends. What language?

ah! sorry - c++ (ms vs2012 express at the moment)

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

It seems like the moment you start really using constructors and destructors you have to subscribe to a bunch of other OO nonsense. At least, this is my own opinion

No, you have to subscribe to some C++ "nonsense" (also known as philosophy) It's not "OO" at all, in the same way that just because you use the "class" keyword to make an aggregate type doesn't mean you are using an OO paradigm in your code.

I hate that you can't take a pointer to constructors and destructors

For good reason, as this would allow you to break some fairly fundamental rules of the language.

Lastly C++ sucks when you define a constructor and yet want to create an array of your object on the stack and you get the error saying that's not possible without a trivial default constructor.

You need a *default* constructor (which need not be trivial); this also follows naturally from the philosophy of the language and is generally what you want. The alternative would be for you to have to have some mechanism to tediously construct every array element, and deal with the fact that an array can thus contain some constructed and some unconstructed items, opening the door for even more exciting undefined behavior.

other than that, are there any "gotcha!"s to look out for?

The biggest mistake I see people make with constructors is failing to understand that the order of initialization in constructor initializer lists is the *member declaration order*, not the order in which members are lexically arranged in the initialization list. If you are initializing some members in terms of other members (which isn't that common), make sure to check this (many compilers will warn you about it).


To enforce a specific initialization time, I explicitly construct the GameStructure after control is actually handed to my program.

that leads to a related constructor question i've had with regard to this "oo port".

i only need one entity list at the program level that i can re-init with each run of the game, not one i create with each game instance i run.

IE in non OO engish: i have an entity list. i only need to declare it once at program start then init it each time i run a new game or load a saved game. i don't need to new it for each game then dispose it when the game ends. this would indicate the need for an explicit init method, rater than using the constructor. is this how its usually done?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

I hate that you can't take a pointer to constructors and destructors. Also a lot of the time there's a huge separation between allocation and initialization, though constructors attempt to package this into a single piece of code. Lastly C++ sucks when you define a constructor and yet want to create an array of your object on the stack and you get the error saying that's not possible without a trivial default constructor.

It seems like the moment you start really using constructors and destructors you have to subscribe to a bunch of other OO nonsense. At least, this is my own opinion.


You appear to have several fundamental misunderstandings about constructors.

1. Constructors have little to nothing to do with allocation.
You allocate something when you define a variable (of that type) or call an appropriate allocation function. As a consequence of defining a variable of a particular type, the constructor for said type will be called. As that is, after all, what you asked it to do. Calling new implicitly invokes the constructor of the type you are attempting to instantiate because that's what you asked it to do "Please allocate enough memory for this type (aligned for the type using fundamental alignment rules) and then construct this type in that memory block."
Should you desire a random blob of memory, you can do that too. malloc and ::operator new both return pointers to blocks of memory aligned to the largest fundamental type it can store.

2. If you define a type and attempt to create an array of said type, guess what? It has to be able to call the constructors of said type. As you attempted to create an array of said type, the assumption is that you would like the constructors to be called. Otherwise you should have just asked for a blob of bytes. If there is no default constructor available then you need to brace initialize it (C++11 and above) and supply the appropriate copy or move constructors.

IE issues - things you don't want to do for some reason in a custom constructor or destructor?


Avoid complex behaviors, substantial allocations, or long tasks. A constructor should be a fairly trivial thing that gets the object in sound working order, ready to be used. Anything that can fail, unless you're writing exception safe code should, generally speaking, not be done in the constructor.

Destructors should be fail safe, same deal as with constructors, but a greater emphasis on "do not do stupid things here that will fail."

Use initializer lists. Avoid overly long parameter lists (prefer passing in structures or pointer/references to structures. Avoid having output parameters on constructors (it's confusing, and see above about errors).

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


the order of initialization in constructor initializer lists is the *member declaration order*, not the order in which members are lexically arranged in the initialization list.

so if i have member variables:

int a;

int b;

and in my constructor i say:

b=5;

a=b*2;

it will process a=b*2 first?

don't recall if i heard of that one before or not. then again, i first learned c++ syntax when it first came out, and haven't really used it much since. so maybe its just yet another thing that i've already forgotten.

aside: in college, my roomies had a calender with quotes for the day, one of which was "I've already forgotten what you'll never even know." pretty harsh huh? <g>


Avoid overly long parameter lists (prefer passing in structures or pointer/references to structures.

yet another point of question in my "OO-port". all i need to do is create one entity list, one generic_graphics_engine, one camera, etc at program start (typically). so the program object would own them, then pass them to a game object. and in general, things that used to be global would be declared at a higher level, and passed as parameters to lower level code. this could mean a lot of parameters. so i was thinking about using pointers to parameter structs the way d3d does. is this considered the best way to pass around such "globals" ?

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


the order of initialization in constructor initializer lists is the *member declaration order*, not the order in which members are lexically arranged in the initialization list.

so if i have member variables:

int a;

int b;

and in my constructor i say:

b=5;

a=b*2;

it will process a=b*2 first?

don't recall if i heard of that one before or not. then again, i first learned c++ syntax when it first came out, and haven't really used it much since. so maybe its just yet another thing that i've already forgotten.


No, that is assignment but Josh is talking about initialization and the initializer list.


struct foo {
    foo() : b(5), a(b*2) {}
 
    int a;
    int b;
};

In this class, a is declared before b and therefore a is also initialized with twice the value of b, before b has been initializer. Code in the constructor is executed top-down, but that code is executed after the initializer list.

This topic is closed to new replies.

Advertisement