I have had the following structure:
struct MyStruct
{
struct Displacement
{
bool isWrapped;
int steps;
};
int initialDuration;
int currentDuration;
Displacement displacement1;
Displacement displacement2;
};
And I was using it like this:
MyStruct obj{2, 2};
auto shared = std::make_shared<MyStruct>(obj);
The problem was that I needed to duplicate first two arguments as initialDuration and currentDuration should be the same when a new object is created. Therefore I have created a CTOR:
struct MyStruct
{
struct Displacement
{
bool isWrapped;
int steps;
};
MyStruct(int duration)
: initialDuration(duration)
, currentDuration(duration)
{
}
int initialDuration;
int currentDuration;
Displacement displacement1;
Displacement displacement2;
};
and then used like this:
auto shared = std::make_shared<MyStruct>(2);
The unexpected thing 1 is: both Displacement members of MyStruct have been initialized with garbage. For one the bool member of true, for the other one - false and ints were some arbitrary numbers.
So though maybe I need to define CTOR for Displacement too. Defined like this:
struct Displacement
{
Displacement() : isWrapped(false), steps(0) {}
bool isWrapped;
int steps;
};
The unexpected thing 2 is: that somewhere else
MyStruct::Displacement d {false, 0};
started to not compile. I don't know aggregate initialization or list-initialization stopped working for a POD struct when I have defined the default CTOR.
Please explain the reasons of those 2 behaviours.
For your 1st question
both Displacement members of MyStruct have been initialized with garbage.
(from comment) There should be a default CTOR for Displacement defined by the compiler which should be called in the CTOR of MyStruct that I have defined, right?
The implicitly-defined default constructor won't do anything here.
If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled) by the compiler, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.
And for MyStruct obj{2, 2};, obj is an automatic object, so (from standard $8.5/12 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 ]
You might want to initialize them as
MyStruct obj{2, 2, {false, 0}, {false, 0}};
For your 2nd question
I don't know aggregate initialization or list-initialization stopped working for a POD struct when I have defined the default CTOR.
Yes it does, unless you provide another ctor which takes std::initializer_list as its parameter. See aggregate initialization
An aggregate is an object of the type that is one of the following
array type
class type (typically, struct or union), that has
no private or protected non-static data members
no user-provided constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)
no base classes
no virtual member functions
no default member initializers (since C++11) (until C++14)
If you have a constructor that is called you need to explicitly initialize all members, or they will be uninitialized. Uninitialized members will have indeterminate values which is what you perceive as garbage. That's how C++ work when you have constructors.
From this implicit default constructor reference:
If the implicitly-declared default constructor is not defined as deleted, it is defined ... by the compiler, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.
Built-in or aggregate types have no constructor, and so can't be constructed or initialized without an explicit initialization.
Your struct is no longer a POD type when you add a default constructor. Among other things, a POD type must have a trivial default constructor.
You need to add a constructor that takes two arguments for your code to start working again. Also note that the default constructor does not 0-initialize members. The reason it worked before with the POD type is that doing aggregate initialization (which only works with POD types) defaults members not explicitly initialized to 0.
Related
Reading cppreference on value initialization, I have come to this:
if T is a class type with no default constructor or with a
user-provided or deleted default constructor, the object is
default-initialized;
And example:
struct T3
{
int mem1;
std::string mem2;
T3() { } // user-provided default constructor
};
Reading article on default initialization
if T is a non-POD (until C++11) class type, the constructors are
considered and subjected to overload resolution against the empty
argument list. The constructor selected (which is one of the default
constructors) is called to provide the initial value for the new
object;
if T is an array type, every element of the array is
default-initialized;
otherwise, nothing is done: the objects with automatic storage
duration (and their subobjects) are initialized to indeterminate
values.
This applies to the example, T is class type, which means the overload resolution should select the candidate to initialize the values ( the user-provided default constructor ), but it is empty, so mem1 should stay with indetermined values (that's true ) but same should be mem2, but that is "default initialized" to "", why is that? Does it work recursively? Every member of T that is class type is subjected to first rule?
I'm quite confused right now.
2)if T is a class type with a default constructor that is neither
user-provided nor deleted (that is, it may be a class with an
implicitly-defined or defaulted default constructor), the object is
zero-initialized and then it is default-initialized if it has a
non-trivial default constructor;
And example:
struct T1
{
int mem1;
std::string mem2;
}; // implicit default constructor
the mem1 is zero-initialized to 0, however what does "non-trivial" default contructor means? mem2 is also default-initialized to "", howevever i am still unsure, what does "non-trivial default constructor" means? The default constructor should be generated by compiler, however how does it decide what is and what is not non-trivial --- if non-trivial default constructor means that it has to initialize objects -- same question as above, does it mean that every object is initialized with default constructor?
same should be mem2, but that is "default initialized" to "", why is that? Does it work recursively? Every member of T that is class type is subjected to first rule?
Your suspicion is correct. When you default initialize the class you default initialize each of its members since no initialization was specified in your constructor. Since std::string has a user provided default constructor that is called and it initializes the string object to nothing.
however what does "non-trivial" default contructor means?
A trivial constructor is a do nothing constructor. For a type T it's constructor is trivial if
The constructor is not user-provided (that is, implicitly-defined or defaulted)
T has no virtual member functions
T has no virtual base classes
T has no non-static members with brace-or-equal initializers.
Every direct base of T has a trivial default constructor
Every non-static member of class type has a trivial default constructor
So in T1's case you do not have a trivial constructor because std::string's default constructor is non trivial.
This applies to the examle, T is class type, which means the overload resolution should select the candidate to inicialize the values ( the user-provided default constructor ), but it is empty, so mem1 should stay with inderermined values (thats true ) but same should be mem2, but that is "default initialized" to "", why is that?
If you omit a data member from the member initializer list then it's default initialized, which for std::string means calling it's default constructor and initializing it to an empty string. Your default constructor has no member initializer list so all the members gets default initialized.
what does "non-trivial" default contructor means?
You can find the requirements for a trivial constructor here. A non-trivial default constructor is just a default constructor that does not respect all of the requirements for a trivial constructor. In short, a trivial default constructor doesn't do anything at all (even under the hood) other than mark the beginning of the lifetime of the object.
does it mean that every object is initialized with default constructor?
As mentioned earlier, the default constructor (like any other constructor) will default initialize any data member that isn't otherwise specified in the member initializer list. Since it's by default empty, default constructors will recursively cause default initialization unless an inner member specifies an alternative initialization using a member initializer list.
I recently ran into a problem with one of my classes because I hadn't set a pointer to NULL in my constructor initialiser list and so it contained rubbish when I ran the program.
However, whilst I know that instances of a built-in type declared on the stack but not initialised will contain random values, I was pretty sure I'd read somewhere that as class members not placed explicitly in the constructor initialiser list would have their default constructors called, for built-in types this would occur too, inserting code like a pseudo-constructor that will on most platforms, set them to zero I also thought I'd read in 'Thinking in C++' somewhere that under certain circumstances before an object is constructed its memory will be zeroed-out, however I appear to be wrong on both occasions.
Please could anyone confirm for me,
a) Does initialisation of members of a built-in type have anything to do with whether a user-defined constructor is defined or not,
b) do members of a built-in type always need to be initialised manually, and
c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?
Also, in researching this, I have seen the terms 'default-initialised' and 'zero-initialised' used - is there a difference between saying:
T a;
and
T a();
? I thought that the first form was just used to prevent ambiguity when the second may be taken by the compiler as a function declaration.
Thank you very much for your time,
stellarpower
First let's go over some examples and the correct terminology.
T a1; // default initialization
T a2{}; // value initialization
T(); // also value initialization
new T; // default initialization
new T(); // value initialization
new T{}; // also value initialization
class C1 {
C1() {}
T x;
}; // no initializer for C1::x; default-initialized
class C2 {
T x;
}; // implicit default constructor default-initializes C2::x
class C3 {
C3() : x() {}
T x;
}; // C3::x will be value-initialized.
class C4 {
C4() : x{} {}
T x;
}; // C4::x will also be value-initialized.
// DANGER
T a(); // declares a function; not value initialization (quirk of C++)
Generally, the rule is that when there is no initializer, it is default initialization, and when the initializer is () or {}, it is value initialization. Note that there is an exception for statics and thread-locals, which I'll discuss later.
For an integer or floating-point type, value initialization sets it to 0. For a pointer type, value initialization sets it to null. Default initialization does nothing for scalar types. Therefore, if an object of scalar type only receives default initialization, then it has indeterminate value.
a) Does initialisation of members of a built-in type have anything to do with wheher a user-defined constructor is defined or not,
The default constructor for a class default-initializes members. A member is also default-initialized when no mem-initializer is explicitly provided for it. The examples C1 and C2 illustrate this. However, note that when a class type is value-initialized, and the class's default constructor is either implicitly defined or explicitly defaulted, the class's members will be zeroed out. This zeroing out occurs only in this case, and doesn't occur for a user-provided default constructor. So the answer to your question is "yes" in this sense.
C1 y1; // y1 is default-initialized; y1.x is indeterminate
C1 y2{}; // y2 is value-initialized; y2.x is indeterminate
C2 y3; // y3 is default-initialized; y3.x is indeterminate
C2 y4{}; // y4 is value-initialized; y4.x is set to 0
C3 y5; // y5 is default-initialized; y5.x is set to 0
C3 y6{}; // y6 is value-initialized; y6.x is set to 0
b) do members of a built-in type always need to be initialised manually, and c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?
I assume you mean "class members with built-in type". I covered a case above in which they are automatically initialized to 0: where the class object is value-initialized and its constructor is not user-provided (or deleted). Another case is when the class object has static or thread-local storage duration. In this case, the members will also be zeroed out at the very beginning, so there is no chance of them ending up with indeterminate value.
The term you are looking for is default initialized. Any instance data member will be default initialized if it is not explicitly initialized in the body of the constructor. What this means depends on whether the data member is a primitive data type or not and where the object instantiation takes place. In case of data members of non-primitive data types, the default constructor will be used (or the compiler will complain if there is no default constructor). In case of data members of primitive data types this means that they will not be initialized if the instantiation takes place on the stack or on the heap. If it is a global or a static variable however, the data members will be set to 0.
By the way, when you write
T a;
then you define a variable a of type T. When you write
T a();
then you declare a function a() which returns T by value.
One way to default initialize all member variables without calling a constructor would be if you have a POD type. For e.g. this is a POD
class Foo {
int num1;
double num2;
bool b;
int *pnum;
};
and if you do
Foo foo = {};
everything will be zero-initialized (b will be false, pnum NULL etc) without calling the (default) constructor.
So
a) a POD type must not have a user defined constructor so in that case yes
b) if you have a POD you don't have to initialize them manually
c) if you have a POD type you can zero-initialize without calling a constructor.
Another way to create a class instance and zero-initialize without calling a constructor would be to allocate the required memory with calloc and cast the pointer to the required type. You should avoid that unless absolutely necessary!
I have searched previous questions, and have not found a satisfying answer to my question:
If I define an empty default constructor for a class, as for example
class my_class{
public:
myclass(){}
private:
int a;
int* b;
std::vector<int> c;
}
my understanding is that if I define an object using the default constructor, say
my_class my_object;
then my_object.a will be a random value, the pointer my_object.b will also be a random value, however the vector c will be a well-behaved, empty vector.
In other words, the default constructor of c is called while the default constructors of a and b is not. Am I understanding this correctly? What is the reason for this?
Thank you!
a and b have non-class types, meaning that they have no constructors at all. Otherwise, your description is correct: my_object.a and my_object.b will have indeterminate values, while my_object.c will be properly constructed.
As for why... by writing a user-defined constructor and not mentioning a and b in the initializer list (and not using C++11 in-class member initializers) you explicitly asked the compiler to leave these members uninitialized.
Note that if your class did not have a user-defined constructor, you'd be able to control the initial values of my_object.a and my_object.b from outside, by specifying initializers at the point of object declaration
my_class my_object1;
// Garbage in `my_object1.a` and `my_object1.b`
my_class my_object2{};
// Zero in `my_object2.a` and null pointer in `my_object2.b`
But when you wrote your own default constructor, you effectively told the compiler that you want to "override" this initialization behavior and do everything yourself.
Since a and b are not objects but primitive datatypes, there is no constructor to call. In contrast, c is an object, so its default constructor is called.
#include<iostream>
using namespace std;
class A {
public:
int i;
};
int main() {
const A aa; //This is wrong, I can't compile it! The implicitly-defined constructor does not initialize ‘int A::i’
}
when I use
class A {
public:
A() {}
int i;
};
this is ok! I can compile it! why I can't compile it when I use the implicitly-defined constructor?
why the implicit-defined constructor does not work?
It does work, but one of the language rules is that it can't be used to initialise a const object unless it initialises all the members; and it doesn't initialise members with trivial types like int. That usually makes sense, since being const there's no way to give them a value later.
(That's a slight simplification; see the comments for chapter and verse from the language standard.)
If you define your own constructor, then you're saying that you know what you're doing and don't want that member initialised. The compiler will let you use that even for a const object.
If you want to set it to zero, then you could value-initialise the object:
const A aa {}; // C++11 or later
const A aa = A(); // historic C++
If you want to set it to another value, or set it to zero without the user having to specify value-initialisation, then you'll need a constructor that initialises the member:
A() : i(whatever) {}
why the implicit-defined constructor does not work?
Because the C++ standard says so:
[dcl.init] paragraph 7:
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.
This ensures that you don't create a const object containing uninitialized data that cannot be initialized later.
To initialize a const-qualified object you need to have a user-provided default constructor or use an initialiser:
const A aa = A();
Here the object aa is initialized with the expression A() which is a value-initialized object. You can value-initialize a class type without a default constructor, because value-initialization will set values to zero if there is no default constructor for the type.
However, the rule in the standard is too strict, as it forbids using implicitly-defined constructors even when there are no data members or all data members have sensible default constructors, so there is a defect report against the standard proposing to change it, see issue 253.
You don't state what compiler you're using. I've tried this with VS2012 and get a warning C4269.
The reason this is a problem is because aa is const. Because you haven't defined a constructor a default one is used and so i can be anything. It also cannot be changed (because aa is const).
If you define a constructor, it is assumed that you are happy with the initialization of i. Although, in this case you haven't actually changed the behaviour.
From this MSDN page
Since this instance of the class is generated on the stack, the initial value of m_data can be anything. Also, since it is a const instance, the value of m_data can never be changed.
Because i is not initialized.
class A
{
public:
A()
{
i =0;
}
int i;
};
"Implicit constructor" means a constructor generated for you automatically and generates an error because it realizes it is not able to initialize the value of i. This can be a no-args constructor, a copy constructor or (as of C++11) a move constructor.
why the implicit-defined constructor does not work?
It works just fine, but it does not decide what your default values are implicitly (and as such, it only calls default constructors for it's members, but not for POD types).
If you want the constructor to initialize your members with certain values you have to explicitly write that (i.e. add a default constructor explicitly).
Making a default (implicit) constructor initialize POD members with a chosen value (like zero for example) would add extra computing cycles (and slow your program down) when you don't need that. C++ is designed to behave as if you (the programmer) know what you are doing (i.e. if you do not initialize your members explicitly, the compiler assumes you don't care what default value you get).
This is very trivial question regarding the use of a constructor in C++. I will present in the form of an interview dialogue (it was difficult to present it in any other forms)
interviewer - what is a constructor?
me - constructor are special functions which makes sure that all objects are initialized before their use.
interviewer - what is an initializer list?
me - that is a list where all the initialization happens. A constructor's body is entered only after all the data members are initialized, or some constructor of all the member objects are called.
interviewer - that means initialization is carried out in initializer list, not inside constructor. But you said constructor initialize the object! Didn't you? Do you want to answer my first question.
me - I think constructor does assignment, it calls assignment operator on already initialized member objects.
So my question to you can be
how initializer list works?
what lies between function's starting address & starting braces [{]?
or just answer me how to convince my interviewer.
Technically speaking, your interpretation is accurate. No members may be initialised from inside the ctor body; only in the ctor-initializer. Any member access in the ctor body may only be assignment.
All members are "initialised" before the ctor body is entered.
However, speaking more broadly, since the body always follows the initializer, it's said that — as a unit — the object is initialised once the constructor has ended... including the body.
Partly this is because, again speaking broadly, you might consider initialisation to include some business logic that you must perform in your ctor body, even though this is not the same as actual initialisation of a data member.
You're overthinking it and allowing the interviewer to confuse you.
Initializing the members of an object is not the same thing as initializing the object itself. Just because the members have sane values doesn't mean the object has been constructed. Until the constructor has completed, the object itself has not been properly initialized.
The main things about an initialisation list are efficiency and code readability.
The readability part is self-explanatory, because you know exactly where to look to see where the values are initialised. The efficiency saving comes in the fact that if you assign them values within the code of the constructor instead, then they will be assigned twice: firstly when the object is created they will be assigned the values provided by that datatype's default constructor, then they will be assigned a new value within your object's constructor. The initialisation list just insures that they are initialised with the value you specified to begin with.
for example, here is an example of an initialisation list I used in a Doubly Linked List implementation:
template <typename T>
LinkedList<T>::LinkedList()
: size(0)
, pHead(NULL)
, pTail(NULL)
{
}
Whereas the less efficient version where size, pHead and pTail get assigned twice is shown below:
template <typename T>
LinkedList<T>::LinkedList()
{
size = 0;
pHead = NULL;
pTail = NULL;
}
Essentially, you are correct but the member initializer shouldn't be considered separate from the constructor. The initializer is a part of the constructor and is called before the main body of the constructor.
When you declare an automatic variable of a built-in type, it is both a definition and a declaration. It's a declaration because an identifier is bound to a type and it's a definition because the compiler allocates storage for it.
int var1; // declares/defines var of type int
int var2 = 0; // declares/defines a var of type int and initializes it to 0
An initializer sets the variable with an initial value when it is defined but it's considered defined before the initializer.
int x = 5;
int y = 5;
int main()
{
int x = x; // x is undefined here not 5 because x refers to itself
int y[y];
int size = sizeof(y)/sizeof(int); // size is 5 (y[5]) since y isn't defined until after the enclosing bracket so y referred to the global y in the declaration.
}
There are some variables that must be initialized however. Constants and references.
It is the same with constructors. The point at which the members have been defined is right before the ctor body. This is the order that members and bases are defined when the constructor is called.
virtual base classes
base class
members--in the order in which they were declared
ctor body is executed
After leaving the constructor body, everything has been initialized.
If you don't use the initializer, then you can assume that its already defined when entering the ctor body but you can't assume it has any particular value. Again, constants and references must be initialized in the member initializer.
The initializer list is part of the constructor. Each constructor has its own.