1

I'm not certain if I have titled my question correctly, so feel free to correct me. I believe that:

  1. Initializing in initialization list is equivalent to
    int a = a;

  2. Initializing in the constructor is equivalent to
    int a; a = a;

But I still can't figure out the reason for the following output:

#include <iostream>
using namespace std;

class test
{
    int a,b;
    public:

    /*
    test(int a, int b): a(a), b(b) {}         //OUTPUT: 3 2
    test(int a, int b) {   a = a; b = b;}     //OUTPUT: -2 1972965730
    test(int c, int d) {   a = c; b = d;}     //OUTPUT: 3 2 
    Hence it does work without this pointer. Unless the variable names are same
    */

    void print()    {   cout<<a<<" "<<b<<"\n";}
};

int main()
{
    test A(3,2);
    A.print();
    return 0;
}

EDITS:

  1. As M.M pointed out: The equivalent of a(a) is this->a = a.

  2. Worth a read: Why should I prefer to use member initialization list?

  3. Two workarounds are:

    test(int a, int b) {   this->a = a; this->b = b;}
    test(int a, int b) {   test::a = a; test::b = b;}
    
Community
  • 1
  • 1
  • 1
    There are 2 differences: Giving a value in the body is not initialization, since the value is already initialized (except for primitive types). In your case, in the 2nd version member 'a' is shadowed by the parameter a, but in initialization list it's not (since you can initialize in the initialization list only members). – Melkon Sep 14 '15 at 07:51
  • 3
    The equivalent of `a(a)` is `this->a = a;` – M.M Sep 14 '15 at 07:52
  • Don’t use same names for class members and parameters of methods. It’s not worth those problems. – Melebius Sep 14 '15 at 07:54
  • 1
    The difference is that initializing lists allows you to directly call constructors for class members and to initialize `const` members. See this [answer](http://stackoverflow.com/questions/926752/why-should-i-prefer-to-use-member-initialization-list) for details. –  Sep 14 '15 at 08:02
  • 1
    Thanks @Melkon and M.M, thats exactly what I wanted to know – Prayansh Srivastava Sep 14 '15 at 08:02
  • @PrayanshSrivastava To avoid this in the future you could tell your compiler to warn you about overshadowing things (e.g. `-Wshadow` for GCC). – Biffen Sep 14 '15 at 08:19

4 Answers4

9
test(int a, int b) {   a = a; b = b;}

This is not correct. It does nothing to the data members. It should be

test(int a, int b) {   this->a = a; this->b = b;}
user207421
  • 305,947
  • 44
  • 307
  • 483
2

In the initialization list the syntax is such that each variable name outside the parens () is a class member. What goes inside the parens is whatever happens to be in scope- be it a class member or a constructor parameter. A parameter will hide a class member.

So you can safely do:

class MyClass
{
    int i;
    MyClass(int i): i(i) {}
    //              ^-must be class member
};

And the compiler will correctly use the parameter from inside the parens to initialize the class member outside the parens.

What happens inside the parens is given the same scope as what happens inside the constructor body.

So:

class MyClass
{
    int i;
    MyClass(int i)
    {
        i = i; // BOTH of those are the parameter i
    }
}

The parameter called i is hiding the class member called i so the class member never gets accessed in the constructor body.

You have to explicitly disambiguate it using this:

class MyClass
{
    int i;
    MyClass(int i)
    {
        // now we set class member i to parameter i
        this->i = i; 
    }
}

All of that is taken care of for you in the syntax of the initializer list:

    //                v-this parameter is hiding the class member
    MyClass(int i): i(i) {}
    //              ^-must be the class member

The initializer list is basically doing: this->i = i for you.

You should always initialize members in the initializer list if possible.

Galik
  • 47,303
  • 4
  • 80
  • 117
0

Try to replace test(int a, int b) { a = a; b = b;} with test(int a, int b) { this->a = a; this->b = b;} My compiler (msvc) produces desired result.

user5263777
  • 43
  • 1
  • 1
  • 3
0

Every function introduces a new scope. When you define your constructor as

test(int a, int b) { a = a; b = b; }

you hide the class members. There is no way for compiler to know that the left a belongs to the class, and the right a is an argument.

When you declare the constructor as

 test(int c, int d) { a = c; b = d; }

you don't have this problem, because there are no more naming clashes.

In the suggested fix, the same reasoning applies:

test(int a, int b) { this->a = a; this->b = b; }

You explicitly qualify that lhs a is a class member by using a this pointer, which is implicitly passed to every member function, including the constructor. However, this code is not equivalent to initialising with the initialisation list. You was correct in your question:

  1. Initializing in initialization list is equivalent to int a = a;
  2. Initializing in the constructor is equivalent to int a; a = a;

This would make a big difference if your member variable was some complex class. Without the initialisation list, you would first create an object using a default constructor and then copy-assign a new value to it, while if you used initialisation list, only copy-construction would happen.

Thus, you should always prefer using initialisation list:

test(int a, int b): a(a), b(b) {}
Maksim Solovjov
  • 3,147
  • 18
  • 28