Initializing std::string members of a class?

Started by
13 comments, last by Kylotan 7 years, 5 months ago
Hi all,
I was cleaning up and organizing my code and found that in many cases I do the following:

class CSomeClass
{
public:
	CSomeClass();
	~CSomeClass();
	
	std::string myMemberString;
};

CSomeClass::CSomeClass()
{
	myMemberString = "";
	// more code
}

Now I've read that the default constructor for a string (without explicitly calling it), already initializes the string to "";
Does that mean that the line 'myMemberString = ""; is waste?

If so, I could clean up, a lot :rolleyes:

For booleans I know it's tricky, they will not be default 'false' in 100% of the cases, so I initialize them in the contructor with 'false' explicitly.
And how about pointers? (which I currently always initialize to 'nullptr' in the constructor)
Any input is appreciated.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Advertisement

Does that mean that the line 'myMemberString = ""; is waste?

Yep. That's not a constructor call, that's an assignment that will be performed after construction.

Probably cheap anyway, but 100% waste.

See http://gcc.godbolt.org/ , enter your code and see asm output for the various compilers. Results depends on compiler version and optimization settings. Recent gcc and clang managed to eliminate separate assignment call, but older gcc couldn't do it.

And how about pointers? (which I currently always initialize to 'nullptr' in the constructor)

Raw pointers should be treated same way as booleans, i.e. explicitly initialize it. Either with value, or make it zero initialized with default initialization. No need to write 'nullptr' or '0' explicitly:


class Foo
{
    bool flag;
    void* ref;

    Foo()
    : flag(), ref()
    { }
};
Thanks, time to clean up then :)

Do you mean () instead of writing out nullptr.
In code, would that be the following (in the constructor implementation, I don't do it inline):

MyPointer();

Instead of:

MyPointer = nullptr;

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

Do you mean () instead of writing out nullptr.
In code, would that be the following (in the constructor implementation, I don't do it inline):

The two are not equivalent in the constructor implementation. In the initializer list, however, they produce equivalent results; the first is value-initialization which for pointers means its value is set to the null pointer, while the second (explicitly) initialises it with a null pointer. Start using the initializer list; some things just cannot be initialized in the constructor body.

Thanks. I think I understand.

Maybe stupid questions, but:

- if you do it with the initializer list, in the class definition, you end with { }, what does this mean when you also implement the constructor in the CPP/ separate from the class definition. Will both be executed?

- what's the advantage of doing it through the initalizer list instead of with a separate implementation?

I also know this 'variant', which uses the initalizer list but it does so in a separate constructor implementation (not within the class definition):


CGameTimer::CGameTimer() : mSecondsPerCount(0.0), mDeltaTime(-1.0), mBaseTime(0), mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false)

Which actually is 'option 3'.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

If you don't initialize a member in the initializer list, its default constructor will be executed as a part of the initialization of the object. Once the initializer list is executed (including the default constructor for members you don't explicitly initialize) the constructor body is executed. The difference between constructing a member in the initializer list and "constructing" it in the body of the containing class' constructor is; the former directly calls the proper constructor, while the latter default constructs the object and then calls the assignment operator of whatever you're assigning to it.

If the member type cannot be default initialized, you must initialize it in the constructor body. If the member type is expensive to default construct, then you pay for unnecessary default initialization and then an assignment to override the value from the default constructor.

edit: To expand on the above a little more. Once the constructor body starts executing, all members have had one of their constructors called and they are all properly constructed. I mentioned in my last post that some things cannot be initialized other than in the initializer list. For example, the base class in an inheritance tree must be called from the initializer list; objects without a default constructor cannot be default constructed; const objects cannot be assigned to in the constructor body since that would change a const object.

Thanks, this clears things up.

Here's an example on how I do it know:


CD3dShaderPack::CD3dShaderPack() : mVS(), mPS(), mGS(), mVSShaderBlob(), mInputLayoutId(0), mIdentifier(0), mIsCreated()
{
	mFlags = D3DCOMPILE_ENABLE_STRICTNESS;
	#if defined( DEBUG ) || defined( _DEBUG )
    mFlags |= D3DCOMPILE_DEBUG;
	#endif
}

There's one last thing I couldn't get confirmed 'googling':

- default constructor for bool, called with () sets it to false

- default constructor for pointer, called with () or (0) sets it to nullptr/NULL

I've tested both, but the result is the same.

Actually, debugging learned that in all cases the pointer already was before I've initialized anything at all.

I guess doing it like this '()' to initialize pointers to nullptr/NULL is safe.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

If you're just setting default values like that, then:


class Foo
{
    bool flag = false;
    void* ref = nullptr;
};
void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
myMemberString = "";

This one falls under one of the somewhat quirky parts of the language. This specific case invokes the constructor that accepts a char* parameter and the default allocator parameter.

When an object is created and immediately assigned, and when the constructor also takes a parameter of a compatible type, a near-universal early optimization was to just call the constructor rather than both the constructor and the assignment.

When the language was standardized back in 1998, that optimization was codified and turned into a requirement, and it remains in the standard today. It is under the section called a converting constructor, partially covered under the 'copy initialization' blanket, and has some interesting rules due to its history. The rule of how and when the constructor is used directly versus when the assignment operator is used can also be modified by the 'explicit' keyword.

cppreference has this little gem explaining it, which applies in this case:


struct A
{
  A(int) { } // converting constructor
  A(int, int) { } // converting constructor (C++11)
  ...
};
...
A a1 = 1; // OK: copy-initialization selects A::A(int)
A a2(2); // OK: direct-initialization selects A::A(int)
A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int)
A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int)
A a5 = (A)1; // OK: explicit cast performs static_cast

As that list hopefully makes clear, each of these options calls the constructor even though several of them appear to use the assignment operation.

For booleans I know it's tricky, they will not be default 'false' in 100% of the cases, so I initialize them in the contructor with 'false' explicitly.
And how about pointers? (which I currently always initialize to 'nullptr' in the constructor)
The rules are consistent, so if it doesn't happen in 100% of the cases then you probably don't understand the rules.
Both bools and pointers must be initialized in your code. The rules about what gets initialized to zero (also meaning false) are described here. Pointers may be statically initialized, such as the line: char* ptr = "Hello, World!"; where the pointer is automatically initialized to point to a piece of static data.
If those rules for static initialization and zero initialization don't apply, initializer lists work, as does direct initialization, copy initialization, default member initialization, and on brand new compilers, differed dynamic initialization. There are plenty of options available to you, but you do need to ensure it is initialized to a value before it is used, otherwise the results are "undefined".
It is further complicated by the fact that modern major operating systems zero out your memory when it is assigned to the process as a security measure. It wouldn't be secure to hand out uninitialized memory that happened to have whatever was left around from other programs, such as usernames and passwords and bank account balances, and there were many viruses and Trojan horses in the 1980s that exploited the security vulnerability. So if you get a freshly allocated block of memory from the OS you might happen to have it zeroed out even though it didn't meet the zero initialization requirements.
Bool is also quirky with undefined values: "Using a bool value in ways described by this International Standard as “undefined,” such as by examining the value of an uninitialized automatic object, might cause it to behave as if it is neither true nor false". While in practice it should evaluate based on whatever happened to be in that memory block at the time, the standard allows for additional undefined behavior.
Take some time to read up on the rules of initialization. Then make sure you always initialize your variables using one of those rules.
Thanks, good to know I'm not alone :) for now I'm OK with always initializing the bools and pointers. Will read the url's for the theory.

So far I couldn't figure out the difference for a ptr between () and (0), because the memory is already NULL before I assign.

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement