2

I have a class named Person which has a name, 4 variables a, b, c, d and a value t which adds a, b, c, d up.

Here is the code that describes my problem:

#include <iostream>

using namespace std;
class person {
    public:
    string name;
    int a;
    int b;
    int c;
    int d;
    int t = a + b + c + d;
};

int main() {
    {
        person p;
        cin >> p.name >> p.a >> p.b >> p.c >> p.d;
        cout << p.t << '\n'; // garbage
    }
    {
        person p;
        string s;
        int A, B, C, D;
        cin >> s >> A >> B >> C >> D;
        p = {s, A, B, C, D};
        cout << p.t << '\n'; // prints the sum
    }

    return 0;
}

In the first block, suppose I receive "Andy", 1, 2, 3, 4 from the user, when printing t, it prints a garbage value. In the second block it prints t = 10 which I expected, the behavior of the first block is unexpected, I don't know why this happens.

Andy
  • 23
  • 2
  • `a`, `b`, `c`, and `d` are all uninitialized when you initialize `t` to the sum in the implicit constructor. Thus you get garbage. In second block you pass them values to the constructor, so they're already initialized by the time the sum is evaluated to initialize `t`. – Cruz Jean Jan 16 '20 at 18:45
  • @CruzJean Init order is well defined. Members are ALWAYS initialized in the order they are declared. This may not be the same order that you TRY to initialize them though. Many compilers will warn if you try to initialize out of order. – user4581301 Jan 16 '20 at 19:33

2 Answers2

2

In the first case your default initializer for t is used. Though when t is initialized via a + b + c + d, the members a,b,c and d havent been initialized yet. You only assign values after creating the object:

// create object with members not initialized
person p;   
// write values
cin >> p.name >> p.a >> p.b >> p.c >> p.d;
// garbage
cout << p.t << '\n'; // garbage

The middle line is irrelevant for what you see for t, because t is initialized only once, before the constructor runs. Setting the other members later has no effect on the value of t.

In the second case you use aggregate initialization. As you supply values for all members but the last, t will again be initialized using the default initializer. In this case, at the time t is initialized all other members already have been initialized (members are initialized in the order they appear in the class definition). Hence you see the correct value.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Here's the example I was working up before this answer made it mostly moot: https://ideone.com/mUbkA5. The main take-away is only once is `calc called` called while values are being assigned. But worrth noting that 3 `person`s are created. The one that works is the Aggregate initialization of a temporary that is then assigned to a pre-existing `p`. – user4581301 Jan 16 '20 at 20:08
  • @user4581301 calling member function for default initializers has to be used with caution. It does not prevent the members from being initialized in order and it is extremely easy to shoot yourself in the foot with this. – 463035818_is_not_an_ai Jan 16 '20 at 20:13
  • @user4581301 here is a question about calling a method in the default initializer, in case you want some more input: https://stackoverflow.com/questions/58958637/is-it-allowed-to-call-a-non-static-member-function-in-a-default-member-initializ – 463035818_is_not_an_ai Jan 16 '20 at 20:14
  • @user4581301 i guess you know that if you want to get the sum of all members that is correct at any time you would simply use a function not a member variable ;) – 463035818_is_not_an_ai Jan 16 '20 at 20:19
  • Did not know that, but makes sense when you stop and think about it. The only reason that function is there is for the console write to show what's going on. – user4581301 Jan 16 '20 at 20:52
0

Using the = operator doesn't create a "rule" that your code will always keep true. For example:

int i = 9;  //i = 9
int j = i + 1;  // right now, j == 10

i = 99;     //now, i is equal to 99, but j has not changed.
            // j doesn't retroactively become 100 because of a previous line of code.

So, in the default constructor, t is initialized to garbage and IT WILL STAY as a random garbage value until it is written to again. t = a + b + c + d; isn't a math formula. It's executed once, and t will hold the value until you write to it again.

MPops
  • 355
  • 3
  • 8
  • "IT WILL STAY as a random garbage value" - OP says otherwise: "In the second block it prints `t = 10`" – ForceBru Jan 16 '20 at 18:57
  • The second time, a garbage value is never written, because they manually set the value. Also, that's a different object. Not the same one – MPops Jan 16 '20 at 18:58
  • They didn't? `p = {s, A, B, C, D};` explicitly initialises `name`, `a`, `b`, `c` and `d`, but not `t` – ForceBru Jan 16 '20 at 19:00
  • @ForceBru Default member initializers are evaluated when the constructor is called (before the constructor body; basically the same as member initializer lists). `a`, `b`, `c`, and `d` are initialized at that time, so `int t = a + b + c + d;` is well-formed and has the expected value. – 0x5453 Jan 16 '20 at 19:04
  • Pretty sure Force Bru knows that already. They're subtly trying trying to prompt a complete and correct answer. – user4581301 Jan 16 '20 at 19:40