I know we can have default value for struct members. For instance, I can set default value for those members:
struct Foo {
int a = 0;
int b = 1;
int c;
}
Suppose I have another constructor for member c:
struct Foo {
int a = 0;
int b = 1;
int c;
foo(int input_c): c(input_c) {}
}
In this case, when I construct a Foo, what's the order of construction? If I do
Foo(100)
My understanding is both a and b are default constructed first then c is assigned 100, is it correct?
------------- Updates ---------------------
Part of my confusion is also the order of execution. For the default values, is it already executed before the constructors?
For instance, I can change my Foo
struct Foo {
int a = 0;
int b = 1;
int c = -1;
foo(int d) {
c += d; // Does c always started with -1?
}
}
The order initialization happens in is quite rigid, it is always the order the members are declared in.
First of all, all default in-class initializers are applied (in the order that members are declared) unless overruled by the initialization list.
Then the constructors initialization list is used and any members listed there are initialized in the order they are declared. If any of those listed members also have in-class initializers, then those don't happen and the initialization list wins and is used to initialize the members.
Then the constructor body is executed. At this point all members are already initialized, either by in-class initialization or the initializer list. But the constructor body can choose to assign new values to those initialized members.
In any case, for a given member foo it will be initialized by the in class initialization (if any) or by the initialization list (if any) or it will be default initialized. Regardless of the method used to initialize it, that initialization will always happen after another member declared before it and before another member declared after it.
For example:
struct s {
int a = 1;
int b = 42;
int c = 666;
int d;
int e;
s() : e(3), b(123) {
c = 7;
}
};
The above will always initialize a first and since it has an in-class initializer, that will be used. It's value will be 1.
b is initialized second. It has an in-class initializer, but the constructors initialization list overrules that. It will be initialized to the value 123.
Then c is initialized to the value 666.
d is uninitialized / or rather; default initialized, which for a int is the same as uninitialized, but for other types like std::string means initialized to an empty string - it depends on the type whether you have a usable value or not.
Then e is initialized to the value 3. This happens last because it is declared last. The order it is listed in in the initialization list of the constructor is irrelevant.
Then the constructor body is executed and c is assigned the value 7. So it is both initialized and subsequently assigned to - this is usually inefficient.
The object construction is now complete.
Yes, the members will be initialized in the order they are declared in the class.
So when you call Foo(100), a and b will be initialized with the default values, and then c will be initialized to 100.
Related
Consider the following class with member initializer list:
class A {
public:
A() : a {b} {
// do something
}
private:
int a {1};
int b {2};
};
The compiler issues a warning that b is used uninitialized in the member initalizer list. This would mean, the member b has been already created but not initialized yet?
Thus, can we assume the following order of execution?
call constructor A()
int a; (create memory for a)
int b; (create memory for b)
a = b; (compiler warning)
b = 2; (assign default 2 to b)
execute constructor's block
This would mean, we actually do not initialize but assign values in step 4 and 5?
EDIT:
If so, what is the advantage of an initializer list, when assignments can also be done in the block after the members have been created prior entering the block?
The sequence you describe is not completely correct. The sequence goes basically as follows:
The memory for the object is allocated; (e.g. using operator new) Note that the memory is not allocated separately. The memory for the object is continuous, possibly with padding in between a and b.
Initializations of members are done where initializers are provided in the initializer list of the constructor or directly at the member declaration. in this case a is declared first then b which is why b has not been initialized at the time you try to read it to initialize a.
The constructor body is executed.
Note that the fields are initialized, not assigned which in case of ints doesn't make a difference, but if the member types would provide assignment operators and constructors there would be a difference between the 2; if you use the initializer list or initialize the member variable when declaring it the constructor is always used.
Please consider the following code:
#include <iostream>
struct A{ // with implicit default constructor
int number;
};
struct B{
int number;
B(){}; // user-provided default constructor
};
int main()
{
A aa = {};
B bb = {};
std::cout << "aa.number: " << aa.number << std::endl;
std::cout << "bb.number: " << bb.number << std::endl;
}
Running the code online
results in the following output:
aa.number: 0
bb.number: 19715
Why is bb.number uninitialized?
I thought that zero initialisation is guaranteed by using ={} ?
I thought that zero initialisation is guaranteed by using ={} ?
That is only true if the type is "correct", which B is not. B bb = {}; will default construct a B and your default constructor, B(){};, doesn't initialize number so no matter what, number will never be initialized because that is how your default constructor works. If you had a "correct" constructor like
B() : number(0) {};
// or use
int number = 0;
B(){};
Then you'd get zero initialization of number when it is default constructed.
This isn't the case with A because A is an aggregate and those come with certain guarantees like zero initialization if an empty braced-init-list, technical name for the {}, is used.
A is an aggregate type, because it doesn't have any user-provided constructors (and fulfills a few other requirements).
Therefore A aa = {}; does not call the implicitly generated default constructor. Instead initialization with brace-enclosed initializer lists performs aggregate initialization, which for an empty list means that the members are initialized as if by a {} initializer, which in turn means for a member of scalar type such as int that it will be initialized to zero.
B is not an aggregate type, because it does have a user-provided constructor.
Therefore B bb = {}; cannot do aggregate initialization and will call the default constructor instead. The default constructor (in either A or B) does not specify an initializer for the members and so the member is default-initialized, which for a fundamental type, such as int, means that it will not be set to any value. Its value will remain indeterminate.
Accessing the indeterminate value, which your program does, causes undefined behavior.
If you declare a constructor yourself, then it becomes that constructor's responsibility to initialize all the members appropriately. The rule that = {} or {} always initializes only holds under the assumption that a user-provided default constructor, if it exists, does the right thing in the sense that it provides sensible initializers to all its members and it doesn't have to mean zero-initialization necessarily.
struct B has a user-provided constructor, so it doesn't get the default constructor that can initialize the members to zero. Your user-provided constructor replaces that, so you have to do the work yourself.
#include <iostream>
using namespace std;
class A{
public:
int data[3];
private:
int cnt;
public:
void put(int v){data[cnt++]=v;}
int take(){int c=cnt;cnt=0;return c;}
};
int main() {
A a;
a.take();
a.put(a.take());
a.put(1);
cout<<a.data[0];
return 0;
}
I understand most of this code, but I got confused by the function a.take(). In the main function, we firstly create an object a. Then we run a.take(). In this function, we first let c = cnt and then cnt is assigned a value of 0.
Why isn't there an error when c is assigned the value of cnt which has not have a value yet.
It is total understandable for me if this function is written as
int take(){cnt=0;c = cnt;return c;}
The author of this code is of the belief that using the initial call to take() to establish the cnt member value of 0 is standard-compliant; they're wrong (at least through C++14, I've not checked C++17)
Per the standard,
8.5 Initializers [dcl.init]
If no initializer is specified for an object, the object is default-initialized. When storage for an object with automatic or
dynamic storage duration is obtained, the object has an indeterminate
value, and if no initialization is performed for the object, that
object retains an indeterminate value until that value is replaced
(5.18). [Note: Objects with static or thread storage duration are
zero-initialized, see 3.6.2. — end note ] If an indeterminate value is
produced by an evaluation, the behavior is undefined except in the
following cases:
None of the exceptions apply to you, so I didn't bother showing them, but you can look them up for confirmation.
The proper way to do this is to establish a determine value for cnt before first use (such as a member initialization list), at which point the worthless take() call can then be removed. In other words,
#include <iostream>
using namespace std;
class A{
public:
int data[3];
private:
int cnt;
public:
A() : cnt(0) {} // HERE
void put(int v){data[cnt++]=v;}
int take(){int c=cnt;cnt=0;return c;}
};
int main()
{
A a;
a.put(a.take());
a.put(1);
cout<<a.data[0];
return 0;
}
Let's walk through main():
A a;
At this step, you created an object a of type A.
What is in a?
(1) a public data member called data, which is an array of int.
(2) a private data member called cnt, which is an int.
Note that at this step, the object a already has these two data members.
What their values are is another matter.
(3) public function members take() and put().
a.take()
Now you have created a, you can use it.
a.take() calls the public member function of take() of a.
In the body of take()
int c=cnt;cnt=0;return c;
c is initialzed with the value of the private data member cnt of a, before it is returned.
So it boils down to the question: What is the value of cnt at this point?
Your question:
Why isn't there an error when c is assigned the value of cnt which has not have a value yet.
Your wording is not accurate. cnt does have a value. This value is undefined, however, in this case. That is, it could be anything. It could be 0, or 42, or -123.
The details:
Since you do not provide a default constructor A() for class A, the compiler would generate a synthesized default constructor for A, which is used to construct a.
Since you do not provide an in-class initializer for cnt (like int cnt = 0;), the default constructor will default initialize cnt.
Since cnt is an int, which is a built-in type, the default initialization rule for a built-in type says, variables of built-in type defined inside a function are uninitialized. The value of an uninitialized variable of built-in type is undefined.
Since a is defined in function main(), cnt has an undefined value.
As cnt is a member variable (non-global), it's value is un-initialized, which means it could be whatever was in that memory location before. It's not an error to use it but the value it will read is effectively garbage.
As a side note, global and static variables are initialized to 0.
I want to know why constant data member of a class need to be initialized at the constructor and why not somewhere else? What is the impact of doing so and not doing so?
I also see that only static constant integral data can be initialized inside the class other than that non of the data members can be initialized inside the class.
for eg:- Suppose below is my class declaration
class A{
int a; // This we can initialize at the constructor or we can set this member by calling "vSet" member function
const int b;
static const int c = 10; //This works fine
public:
A();
~A();
void vSet(int a);
int iAdd();
void vDisplay();
};
And the constructor is defined as mentioned below:-
Edited Section: As previous constructor definition example was wrong
A::A():a(1),b(9){}
Please correct me if I am wrong.
Thanks in advance.
A::A(){
a = 1;
b = 9; // Why we need to initialize this only at the constructor.
}
Is not initialization but it is Assignment.
a and b are already constructed and you assign them values in this case. The const qualifier demands that the variable not be changed after its initialization, allowing this assignment would break that contract.
This is Initialization using Member Initialization list.
A::A():a(1),b(9)
{}
You might want to have a look at this answer of mine to know the difference:
What is the difference between Initializing and Assignment inside constructor?
As for another question, regarding only static constant integral data can be initialized inside the class, please read this answer of mine which explains in greater detail:
Why I can't initialize non-const static member or static array in class?
What you're doing is not initialization. It is assignment, so b=9 will NOT even compile, because b is a const, so cannot be assigned any value to it. It should be initialized, and to do that, use member-initialization list as:
A::A() : a(1), b(9)
{ // ^^^^^^^^^^^^ this is called member-initialization list
}
In C++11, you can use in-place initialization as:
class A{
int a = 1; //C++11 only
const int b = 9; //C++11 only
static const int c = 10; //This works fine (both in C++03 and C++11)
//...
};
Because it is constant, it's value cannot be changed. Initializing anywhere else other than the constructor would mean a default initialization, followed by an assignment, which is not permitted for a constant. It would be the equivalent of doing this:
const int i; // OK
i = 42; // Error!
Note that in C++11 it is perfectly OK to do this:
struct Foo {
const int i=42;
const double x = 3.1416;
};
But this follows the same rules, i.e there is no assignment.
A const data is a data that can never be changed. It is initialized once, and then keeps the same value forever.
Because of that, you can't just assign a value to it anywhere.
The constructor, however, is where initialization goes. So here, there is an exception, and you are able to assign a value to your const data. You can do this the classical way, or as many said, as initialization list.
Now, why you can't do this in the class definition (unlike, say, Java) when the variable is not static is another problem, that I know no answer to.
When the body of your constructor is entered, all members and sub-objects are already initialized. The only thing the constructor body can do is to change them – and that is, obviously, not allowed for const members.
You can, however, use an initializer list.
You can't initialize const members outside of a constructor because, well, they're const. By definition, you can't modify them after they've been initialized, and after the object has been constructed, anything that tries to set the value is a modification.
const values are meant to be rvalues, so they cannot appear on the right part of an expression due its constness.
so, when you use this expression on the constructor's body
A::A()
{
a = 1;
b = 9; // Why we need to initialize this only at the constructor.
}
You're using the const value as a lvalue, just as Als mentioned before. The fact is that you're trying to assing a new value to a variable that isn't allowed to change it's value afther it's lifetime had begun.
The correct way to assign values to a constant data member is in the ctor initializer list, that is, BEFORE the lifetime of the member value begins (as mentioned by Nawaz):
A::A() :
a(1),
b(9)
{
}
Finally, on the C++11 standard you're allowed to initialize all data members where it was declared, just like the static const ones.
Just a simple question about c++ coding style,
for example, all member variables of a class will be called with the default constructor in the initialization list if we don't do anything else. B default constructor will be called and value will be set to 0, int();
class A
{
A();
private:
B b;
int value;
}
However my question is, even do the default constructor will be called is it a good habit to always do it yourself or does it only add extra lines to the code
A::A() : b(), value() {}
You are touching on one of the sticky corners of C++.
The initialization of POD values in objects is sticky and depends on a few things.
Even I am not sure I can get all the rules correct but I believe #Steve Jessop once wrote an article about here on SO (though I can currently find it).
But some examples:
This class will always be initialized b == false and value = 0.
class A
{
A() : b(), value() {}
B b;
int value;
};
Without an explicit default constructor it is more complex:
Here the compiler will generate a default constructor for you. But how the compiler generated default constructor works depends on the situation. The compiler generated default constructor can do two different forms of initialization and which is used depends on context:
Zero Initialization (All POD members are zero'ed out)
Value Initialization (All POD members are left undefined)
Example:
class B
{
B b;
int value;
};
// Variables of static storage duration (notably globals)
// Will be zero initialized and thus b == false and value = 0
B global; // Initialized
int main()
{
// Object of automatic storage duration or dynamic storage duration
// These will depend on how they are declared.
// Value Initialization (POD re-mains undefined)
B bo1; // b/value undefined
B* bp1 = new B; // b.balue undefined
// Zero Initialization
B bo2 = B(); // b = false, value = 0
B* bp2 = new B(); // b = false, value = 0
// Note: The most obvious syntax for zero initializing a local object
// does not work as it is actually interpreted as a forward
// declaration of a function;
B bo3();
}
It's a good idea to leave the default constructor out. The compiler-generated constructors are much more reliable and maintainable than writing your own versions, and apart from anything else, it's a flat out waste of time to write code the compiler could write for you. If you don't need any construction logic, then don't write a constructor.
However, the int will not be initialized unless you do it, which is a sucky reason to have to write a constructor.
By default int variables are not initialized with a value - you have to do it yourself.
So when you don't set the member variable "value" to some value in the constructor it is left uninitialized.
The default constructor will only be called for class types with default constructors, not for primitives.