C++: does the order in a function body matter - c++

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

Related

Why does my array initializer not work unless I create a separate variable for it?

The following C++ code compiles with no errors or warnings, but for some reason when I print out the contents of foo.arr I just get junk values. It looks like the array member is not being properly initialized.
template<int n> class C{
public:
const uint8_t (&arr)[n];
const int length = n;
C(const uint8_t (&arr_arg)[n]):arr(arr_arg)
{}
};
int main() {
C<5> foo{{1,2,3,4,5}};
for(int i =0;i<foo.length;i++){
printf("foo.arr[%d]=%2x\r\n",i,foo.arr[i]);
};
}
But if I write it like this, foo.arr is correctly initialized when I print out the results.
int main() {
const uint8_t x[]{1,2,3,4,5};
C<5> foo{x};
for(int i =0;i<foo.length;i++){
printf("foo.arr[%d]=%2x\r\n",i,foo.arr[i]);
};
}
Why does the second case work, but the first one doesn't?
One would think that the compiler would create a block of five bytes for the constant data {1,2,3,4,5} and then the initializer for foo would point foo.arr to that block of bytes. It seems that whatever foo.arr is pointing either not where that data is, or else that data doesn't exist any more by the time I print it out (possibly overwritten).
C<5> foo{{1,2,3,4,5}};
The constructor's parameter is a reference to a temporary object that gets destroyed after the constructor call finishes. All subsequent reference to this object results in undefined behavior.
C<5> foo{x};
Here, the constructor's parameter is a reference to an object that remains in scope and continues to exist as long as it is subsequently referenced and used.
It seems that ... data doesn't exist any more by the time I print it out (possibly overwritten).
That is, indeed, what's happening.

default value in struct and the constructor order

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.

C++/VisualStudio2017 : Override c4700 compiling error

I'm working on a c++ project for the university and I get problems with an uninitialized pointer in the following code :
#include <iostream>
using namespace std;
struct t_coord {
double valeur;
int indice;
t_coord *suiv;
};
struct t_vecteur {
int dimension;
double defaut;
t_coord *tete;
};
void initialiser (t_vecteur tv, int dimension, double defaut) {
tv.dimension = dimension;
tv.defaut = defaut;
tv.tete = nullptr;
}
int main () {
t_vecteur tv;
initialiser(tv, 5, 3.14);
return 0;
}
Is there anyway to override c4700 error in VisualStudio so I can use my initialization function "initialiser()" ? The final compilation will be done on GNU which does not take in account uninitialized variables and it is specified in the project that I have to create a function to initialize my t_vecteur.
Thanks in advance
The struct t_vecteur is an aggregate, and as such the instance tv is not initialized (dimension, defaut and tete, being primitive data types, contain indeterminate data).
Then you pass it by-value to the function initialiser, which reads from tv when making a copy, which invokes undefined behavior.
The relevant phrase of the standard is in [dcl.init]/12:
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
If an indeterminate value is produced by an evaluation, the behavior is undefined
To fix it (and the C4700 uninitialized variable warning), pass tv to initialiser by-reference.
void initialiser (t_vecteur& tv, int dimension, double defaut) {
Passing by-reference is not reading from tv, hence doing so is not UB anymore.

Why constant data member of a class need to be initialized at the constructor?

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.

Initialisation and assignment

What EXACTLY is the difference between INITIALIZATION and ASSIGNMENT ?
PS : If possible please give examples in C and C++ , specifically .
Actually , I was confused by these statements ...
C++ provides another way of initializing member variables that allows us to initialize member variables when they are created rather than afterwards. This is done through use of an initialization list.
Using an initialization list is very similar to doing implicit assignments.
Oh my. Initialization and assignment. Well, that's confusion for sure!
To initialize is to make ready for use. And when we're talking about a variable, that means giving the variable a first, useful value. And one way to do that is by using an assignment.
So it's pretty subtle: assignment is one way to do initialization.
Assignment works well for initializing e.g. an int, but it doesn't work well for initializing e.g. a std::string. Why? Because the std::string object contains at least one pointer to dynamically allocated memory, and
if the object has not yet been initialized, that pointer needs to be set to point at a properly allocated buffer (block of memory to hold the string contents), but
if the object has already been initialized, then an assignment may have to deallocate the old buffer and allocate a new one.
So the std::string object's assignment operator evidently has to behave in two different ways, depending on whether the object has already been initialized or not!
Of course it doesn't behave in two different ways. Instead, for a std::string object the initialization is taken care of by a constructor. You can say that a constructor's job is to take the area of memory that will represent the object, and change the arbitrary bits there to something suitable for the object type, something that represents a valid object state.
That initialization from raw memory should ideally be done once for each object, before any other operations on the object.
And the C++ rules effectively guarantee that. At least as long as you don't use very low level facilities. One might call that the C++ construction guarantee.
So, this means that when you do
std::string s( "one" );
then you're doing simple construction from raw memory, but when you do
std::string s;
s = "two";
then you're first constructing s (with an object state representing an empty string), and then assigning to this already initialized s.
And that, finally, allows me to answer your question. From the point of view of language independent programming the first useful value is presumably the one that's assigned, and so in this view one thinks of the assignment as initialization. Yet, at the C++ technical level initialization has already been done, by a call of std::string's default constructor, so at this level one thinks of the declaration as initialization, and the assignment as just a later change of value.
So, especially the term "initialization" depends on the context!
Simply apply some common sense to sort out what Someone Else probably means.
Cheers & hth.,
In the simplest of terms:
int a = 0; // initialization of a to 0
a = 1; // assignment of a to 1
For built in types its relatively straight forward. For user defined types it can get more complex. Have a look at this article.
For instance:
class A
{
public:
A() : val_(0) // initializer list, initializes val_
{}
A(const int v) : val_(v) // initializes val_
{}
A(const A& rhs) : val_(rhs.val_) // still initialization of val_
{}
private:
int val_;
};
// all initialization:
A a;
A a2(4);
A a3(a2);
a = a3; // assignment
Initialization is creating an instance(of type) with certain value.
int i = 0;
Assignment is to give value to an already created instance(of type).
int i;
i = 0
To Answer your edited Question:
What is the difference between Initializing And Assignment inside constructor? &
What is the advantage?
There is a difference between Initializing a member using initializer list and assigning it an value inside the constructor body.
When you initialize fields via initializer list the constructors will be called once.
If you use the assignment then the fields will be first initialized with default constructors and then reassigned (via assignment operator) with actual values.
As you see there is an additional overhead of creation & assignment in the latter, which might be considerable for user defined classes.
For an integer data type or POD class members there is no practical overhead.
An Code Example:
class Myclass
{
public:
Myclass (unsigned int param) : param_ (param)
{
}
unsigned int param () const
{
return param_;
}
private:
unsigned int param_;
};
In the above example:
Myclass (unsigned int param) : param_ (param)
This construct is called a Member Initializer List in C++.
It initializes a member param_ to a value param.
When do you HAVE TO use member Initializer list?
You will have(rather forced) to use a Member Initializer list if:
Your class has a reference member
Your class has a const member or
Your class doesn't have a default constructor
Initialisation: giving an object an initial value:
int a(0);
int b = 2;
int c = a;
int d(c);
std::vector<int> e;
Assignment: assigning a new value to an object:
a = b;
b = 5;
c = a;
d = 2;
In C the general syntax for initialization is with {}:
struct toto { unsigned a; double c[2] };
struct toto T = { 3, { 4.5, 3.1 } };
struct toto S = { .c = { [1] = 7.0 }, .a = 32 };
The one for S is called "designated initializers" and is only available from C99 onward.
Fields that are omitted are automatically initialized with the
correct 0 for the corresponding type.
this syntax applies even to basic data types like double r = { 1.0
};
There is a catchall initializer that sets all fields to 0, namely { 0 }.
if the variable is of static linkage all expressions of the
initializer must be constant expressions
This {} syntax can not be used directly for assignment, but in C99 you can use compound literals instead like
S = (struct toto){ .c = { [1] = 5.0 } };
So by first creating a temporary object on the RHS and assigning this to your object.
One thing that nobody has yet mentioned is the difference between initialisation and assignment of class fields in the constructor.
Let us consider the class:
class Thing
{
int num;
char c;
public:
Thing();
};
Thing::Thing()
: num(5)
{
c = 'a';
}
What we have here is a constructor that initialises Thing::num to the value of 5, and assigns 'a' to Thing::c. In this case the difference is minor, but as was said before if you were to substitute int and char in this example for some arbitrary classes, we would be talking about the difference between calling a parameterised constructor versus a default constructor followed by operator= function.