Reputation: 45
#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;}
Upvotes: 2
Views: 145
Reputation: 2205
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.
Upvotes: 1
Reputation: 66204
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;
}
Upvotes: 2
Reputation: 4099
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
.
Upvotes: 0