Skip to main content
gamedev.net
🔒 Locked

Is that way of using constructor really better ?

Started by Tibetan Sand Fox 28 replies 7.9k views
Original Post
Tibetan Sand Fox
Tibetan Sand Fox
Hello.
Not so long ago I started to learn D3D11 and at the moment I'm swimming through "Beginning DirectX 11 Game Programming" book. I found one thing, which I'm curious about. Author said that constructor’s member initialize list can be more efficient and is better programming practice than letting a constructor do the job in default way.

My question is as in topic, is that way of initializing members really better ?

Example from book:

D3DBase::D3DBase() : driverType_( D3D_DRIVER_TYPE_NULL ), featureLevel_( D3D_FEATURE_LEVEL_11_0 ),
d3dDevice_( 0 ), d3dContext_( 0 ), swapChain_( 0 ), backBufferTarget_( 0 )


Thank you in advance for any help.
Bregma
Bregma
Yes.

Constructing your members with initial values is always as good as or better than constructing your members with default values and then assigning them an "initial" value later.

Why?

(1) it's often less work (cpu cycles, memory)
(2) it's often less code... the most efficient code is code you don't write
(3) your invariants are never ever violated (the object is never in an invalid state)
(4) some kinds of members, eg. references, must be initialized using an initializer list, so just do them all that way for consistency

There are some coding standards, such as the famous Google guidelines, that discourage you from initializing member variables using initializer lists. Such standards generally forbid the use of other features introduced into the language in the early 1990s and were obviously written either by a summer student or someone trying hard to sabotage the use of C++ in their organization to the benefit of their own religious language fervour. You should aim not to be one of those people.
Stephen M. Webb
Professional Free Software Developer
Servant of the Lord
Servant of the Lord
Not just references, but also inherited classes must be constructed in initializer lists:
SubClass::SubClass(int blah) : BaseClass(blah) { }

With C++11 you can use "constructor delegating" (to call one constructor from another constructor) - this must also be done by initializer lists:
class MyClass()
{
public:
MyClass() : MyClass("Unnamed", 5ft + 9in, BrownColor, 160lbs) {}
MyClass(std::string name, Color eyeColor) : MyClass(name, 5ft + 9in, eyeColor, 160 lbs) {}
MyClass(std::string name, int height, int weight) : MyClass(name, height, BrownColor, weight) {}
MyClass(std::string name, int height, Color eyeColor, int weight) : name(name), height(height), eyeColor(eyeColor), weight(weight) {}
}

[size=2]The above is C++11 specific code.

How-so ever! Also in C++11, you get 'Non-static data member initializers'. *squeals with joy*

This will allow you to do this instead:
class MyClass()
{
public:
MyClass() : {}
MyClass(std::string name, Color eyeColor) : name(name), eyeColor(eyeColor) {}
MyClass(std::string name, int height, int weight) : name(name), height(height), weight(weight) {}
MyClass(std::string name, int height, Color eyeColor, int weight) : name(name), height(height), eyeColor(eyeColor), weight(weight) {}

private:
std::string name = "Unnamed"; //I get to do default initialization right in the class body! Sweet!
int height = 5ft + 9in;
int weight = 160lbs;
Color eyeColor = BrownColor;
}
Cornstalks
Cornstalks
[edit: I was waaaay off on my original comment]

@Servant of the Lord: you should prefix literals with an underscore, as literal suffixes not starting with an underscore are reserved for future use. But thanks for reminding me that they exist!
Ryan_001
Ryan_001
Most modern compilers will optimize the trivial stuff. So I only stick important things (large items, parent classes, references, ect...) in the initializer list, as I really don't like them. My big issue is that the order items are initlialized in the initializer list is not the same order as they are listed. Which for dependent items can really lead to some hard to track down bugs.
mrbastard
mrbastard

My big issue is that the order items are initlialized in the initializer list is not the same order as they are listed. Which for dependent items can really lead to some hard to track down bugs.


Ah, but should initialisation be in the initialiser list order, or the order you declared them in the class declaration?

FWIW many coding standards suggest keeping your initialiser lists in the same order as your member declarations for exactly this reason.
[size="1"]
Ryan_001
Ryan_001

[quote name='Ryan_001' timestamp='1349347882' post='4986714']
My big issue is that the order items are initlialized in the initializer list is not the same order as they are listed. Which for dependent items can really lead to some hard to track down bugs.


Ah, but should initialisation be in the initialiser list order, or the order you declared them in the class declaration?

FWIW many coding standards suggest keeping your initialiser lists in the same order as your member declarations for exactly this reason.
[/quote]

Their order in the class definition is either for readability/clarity, or compactness/cache coherency. No one orders their variables in intialization order, at least, no one outside of C++. Not to mention dependencies can change based on the constructor that is chosen. If the order of initialization occurs in order of the intialization list, then at least you have some control in regards to dependencies. Course IMO the best solution is just to use intializtion lists when absolutely necessary, use standard C++ code the rest of the time.
jbadams
jbadams

use intializtion lists when absolutely necessary, use standard C++ code the rest of the time.

The initializer lists are standard C++ code.
- Jason Astle-Adams
Bregma
Bregma

[quote name='Ryan_001' timestamp='1349347882' post='4986714']
My big issue is that the order items are initlialized in the initializer list is not the same order as they are listed. Which for dependent items can really lead to some hard to track down bugs.


Ah, but should initialisation be in the initialiser list order, or the order you declared them in the class declaration?

FWIW many coding standards suggest keeping your initialiser lists in the same order as your member declarations for exactly this reason.
[/quote]
That, and because the language standard clearly and unambiguously specifies that member data is initialized in the order of declaration regardless of the order in which they appear in initializer lists. If there is a compiler that does not issue a warning when you put initializers out of order, you should switch to one that does. If you ignore the warnings the compiler gives you, blame only yourself.

If your coding standards suggest you write bad code, consider dropping that standard.

Consider also that if other people have to maintain your code, and they have become used to good practices such as initializing all member data in initializer lists, then you increase the maintenance cost of your software.
Stephen M. Webb
Professional Free Software Developer
clb
clb
One gotcha though related to initializer lists is that exception safety is not possible if allocating memory to raw pointers.

Consider

Bar::Bar()
{
throw SomeException();
}

class Foo
{
int *somePointer;
Bar bar;
};

Foo::Foo()
:somePointer(new int[10])
{
}

Foo::~Foo()
{
delete[] somePointer;
}


The above code has a memory leak, somePointer is never freed since construction of Bar throws an exception.
SiCrane
SiCrane
No, it's possible to handle that case, it's just ugly, annoying and doesn't scale well.

Foo::Foo()
try : somePointer(new int[10])
{
// constructor body
} catch (SomeException & s) {
delete [] somePointer;
throw;
}
Bregma
Bregma

One gotcha though related to initializer lists is that exception safety is not possible if allocating memory to raw pointers.

....

The above code has a memory leak, somePointer is never freed since construction of Bar throws an exception.

It's true that you can write bad code in any language. In this case, the language is C.

The C++ answer to that is do not use C-style raw pointers.

Bar::Bar()
{
throw SomeException();
}

class Foo
{
std::unique_ptr<int[]> somePointer;
Bar bar;
};

Foo::Foo()
:somePointer(new int[10])
{
}

Foo::~Foo()
{
}

Wow, less code, exception safe.

Of course, there's the response "but I prefer to write bad code and bad code doesn't work, therefore C++ is bad", but all that kind of argument does for me is make me respect you less.
Stephen M. Webb
Professional Free Software Developer
Servant of the Lord
Servant of the Lord

The C++ answer to that is do not use C-style raw pointers.

QFT.
Of course, there's the response "but I prefer to write bad code and bad code doesn't work, therefore C++ is bad"[/quote]

"I get my hands dirty when I try to garden without a shovel!"
Then use a shovel! It's sitting two feet away from you...
"C++ is bad, because it lets you dig with your hands if you want to. *My* language glues the shovel to your hands."

C++ isn't intended to, nor designed for, protecting users from themselves.
Now excuse me while I go try to make a pot of coffee with a shovel still glued to my hands.
mrbastard
mrbastard

If there is a compiler that does not issue a warning when you put initializers out of order, you should switch to one that does.


FYI Visual c++ 2012 does not issue a warning about this with /wall (and 'language extensions' disabled fwiw). I know gcc does, and I agree that it's a useful warning.
[size="1"]
tanzanite7
tanzanite7


int height = 5ft + 9in;
int weight = 160lbs;


O_o, units!? That would be absolutely awesome. Is this some c++11 thing? How does that work? Ref? (Google gave nothing)
Cornstalks
Cornstalks

[quote name='Servant of the Lord' timestamp='1349319567' post='4986638']

int height = 5ft + 9in;
int weight = 160lbs;


O_o, units!? That would be absolutely awesome. Is this some c++11 thing? How does that work? Ref? (Google gave nothing)
[/quote]
They are user-defined literals. Note that they should be prefixed with an underscore to be technically right. You have to declare and define the custom literals yourself.
Servant of the Lord
Servant of the Lord
Yep, they are a C++11 feature. Suffixes only, no prefixes at this point. Here's wikipedia's thoughts.
Alas I can't use it yet, since I'm still on MinGW/GCC 4.6, and those (and most of the above C++11 features I mentioned) are on 4.7. sad.png

@Cornstalks: Thanks for the tips about the underscore! The user-defined literals should've also (better/safer coding) returned a user-defined type, like Length, instead of putting it into a generic int: Length myHeight = (4_feet + 8_inches);
Tibetan Sand Fox
Tibetan Sand Fox
Thanks a lot for all those MUCH useful answers ! I didn't even expect to learn so much when I posted this thread. I know everything I wanted to know and even more. Well, I gave most of you +rep. Also you convinced me to use initializer lists more often. It's just drop in the ocean of knowledge I'd like to have, but, well, that's a step. Thank you.
Slavik81
Slavik81

[quote name='Bregma' timestamp='1349356831' post='4986749']
If there is a compiler that does not issue a warning when you put initializers out of order, you should switch to one that does.


FYI Visual c++ 2012 does not issue a warning about this with /wall (and 'language extensions' disabled fwiw). I know gcc does, and I agree that it's a useful warning.
[/quote]
Yeah, msvc is the only compiler I know of that doesn't support that warning. For gcc, clang and intel, the flag is -Wreorder.

You could vote for it if you want Microsoft to add it.
taz0010
taz0010
Most modern compilers will optimize the trivial stuff. So I only stick important things (large items, parent classes, references, ect...) in the initializer list, as I really don't like them. My big issue is that the order items are initlialized in the initializer list is not the same order as they are listed. Which for dependent items can really lead to some hard to track down bugs.[/quote]

Why is this being voted down? His gripe about the initialiser list order not being the actual initialisation order is perfectly valid. It's intuitive to assume that the variables are going to be initialised in the order that the initialisation code is written - which is exactly the case whenever you initialise something using the equals sign.

Also mentioned in this topic, ordering your members for alignment or caching reasons can potentially break your code if one member was initialised using the value of another. And since headers are used, the potential error will not even be visible in the file where the initialisation code is actually written.

Personally if I have primitive variables that I need to initialise in a specific order, I'm not going to rely on the class definition order to do it, and that means good old fashioned assignment with the = operator.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.