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;}
    
6
  • 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). Commented Sep 14, 2015 at 7:51
  • 3
    The equivalent of a(a) is this->a = a; Commented Sep 14, 2015 at 7:52
  • Don’t use same names for class members and parameters of methods. It’s not worth those problems. Commented Sep 14, 2015 at 7: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 for details. Commented Sep 14, 2015 at 8:02
  • 1
    Thanks @Melkon and M.M, thats exactly what I wanted to know Commented Sep 14, 2015 at 8:02

4 Answers 4

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;}
Sign up to request clarification or add additional context in comments.

2 Comments

Just changing the variable name works without this: i.e test(int c, int d) { a = c; b = d;}
@PrayanshSrivasta Certainly, and so do various other things. Using the initializer list is the correct way.
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.

Comments

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.

Comments

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) {}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.