Sign in to follow this  

Initializing std::string members of a class?

This topic is 410 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites

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()
    { }
};

Share this post


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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Edited by cozzie

Share this post


Link to post
Share on other sites

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.

Edited by Brother Bob

Share this post


Link to post
Share on other sites

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.

Share this post


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

Share this post


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

Share this post


Link to post
Share on other sites

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

That's probably a compiler debug setting. Some compilers will initialise pointers to 0xCDCDCDCD or 0 in debug mode.

 

As for what you should use, it depends.

 

Is null a legitimate value (i.e. the pointer is optional)? use nullptr.

If you expect that your pointer will always have a value, then use a reference and initialise that on construction instead.

Share this post


Link to post
Share on other sites

Thanks. I just want to make to make sure it's null(ptr) at construction. But I'm not 100% sure that both () and (0) in the initializer list do that.

If you want to make sure it's null, then use 

SomeClass::SomeClass()
 : pointer(nullptr)
{
}

It's explicit and self-documenting.

Share this post


Link to post
Share on other sites

It is under the section called a converting constructor

 

Your code example is entirely of variable declarations - but the original post had it as an assignment operator inside the constructor, after the object was already created. In that case I would expect the unoptimised version to perform a construction and then an assignment.

Share this post


Link to post
Share on other sites

This topic is 410 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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