Constructor initialization Vs assignment

Let us consider the following classes

class test1
{
    private:
        int a;
        int b;
    public:
        test1():a(0),b(0){}
};

class test2
{
    private:
        int a;
        int b;
    public:
        test2()
        {
            a=0;
            b=0;
        }
};

Now, I know that test1() constructor is the right way to initialize the data members of a class, because in test2() we are performing assignment and not initialization. My questions are:

  1. What might go wrong if we perform assignment instead of initialization?
  2. Doesn't the compiler internally perform assignment in case of test1() constructor? If not, then how are these initialized?

Solution 1:

What might go wrong if we perform assignment instead of initialization?

Some class types (and also references and const objects) can't be assigned; some can't be default-initialised; some might be more expensive to default-initialise and reassign than to initialise directly.

Doesn't the compiler internally performs assignment in case of test1() constructor? If no then how are these initialized?

In the case of primitive types like int, there is little or no practical difference between the two. Default-initialisation does nothing, and direct-initialisation and assignment both do essentially the same thing.

In the case of class types, default-initialisation, assignment and direct-initialisation each call different user-defined functions, and some operations may not exist at all; so in general the two examples could have very different behaviour.

Solution 2:

For your example there is no real different because you are initializing plain integers.

But assume these integers are objects with constructors, then the compiler would generate the following calls:

// test1
a::copy_constructor(0);
b::copy_constructor(0);


// test2
a::default_constructor();
b::default_constructor();
a::operator = (0);
b::operator = (0);

So depending on your objects test2 could have a huge performance impact. Also by initializing your objects in the initializing lists guaranties that your variables have the data when you enter the constructor. One 'drawback' of the initializer list is that the it is executed in the order that the variables are declared and not in the order of the initializer list, so it could be that you don't want to use the initializer list.

Solution 3:

A third variant utilizing direct-list-initialization. Advantages are

  1. variables don't have to be default-initialized in the constructor
  2. the constructor and not the copy/move assignment operator is used to construct the members (as the other answers said, it doesn't matter for the primitive type of this example)

So this is less error-prone than both variants in the question:

#include <iostream>
class Test3
{
    public: // Made public so we can easily output the vars. No struct is used to be consistent with question.
        int a { 4 }; // No need for separate initialization in constructor.
        int b { 2 }; // No need for separate initialization in constructor.
};
int main()
{
    const auto t = std::move(Test3()); // Implicitly-declared default constructor + implicitly-declared move assignment operator
    std::cout << t.a << t.b;
}

Output is 42.