Why constructor is being called twice

But when I am assigning obj2 = 100, how the compiler is allowing initializing an integer to a class object?

This is because when you do the following:

obj2 = 100;

this one will first call abc(int x) to generate an object of the class, then call the default copy assignment operator (since no user-defined is provided) to assign the value 100 to existing obj2. After the assignment, the temporary object is destructed.

If you do not desire this effect, mark the constructor as explict to avoid implicit calls.

explicit abc(int x) {
    //do something
}

 obj2 = 100;

You defined a constructor which takes an int. This allows for an implicit conversion from int to abc. This requires the creation of a new object. It doesn't just magically set a field in the existing object by calling a constructor; constructors construct new objects.

EDIT: Correct sequence of events from @Steve Jessop

A new instance is created, then copy-assigned to the original, then the temporary (not the original) is destroyed. The copy assignment does indeed magically set both fields in the existing object.


Let us play show and tell, so let us instrument all special members:

#include <iostream>

class abc{
public:
    int a, b;

    abc()
    { std::cout << "Default constructor\n"; a = 0; b = 0;}

    abc(int x)
    { std::cout << "Int constructor\n"; a = x;}

    abc(abc const& other): a(other.a), b(other.b)
    { std::cout << "Copy constructor (" << a << ", " << b << ")\n"; }

    abc& operator=(abc const& other) {
      std::cout << "Assignment operator (" << a << ", " << b << ") = (" << other.a << ", " << other.b << ")\n";
      a = other.a;
      b = other.b;
      return *this;
    }

    ~abc()
    {std::cout << "Destructor Called\n";}
};

int main()
{
    abc obj1;
    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
    abc obj2;
    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
    obj2 = 100;
    std::cout << "OBJ2 " << obj2.a << "\n";

    return 0;
}

And we obtain this output:

Default constructor
OBJ1 0...0
Default constructor
OBJ2 0...0
Int constructor
Assignment operator (0, 0) = (100, 0)
Destructor Called
OBJ2 100
Destructor Called
Destructor Called

So, let's reconcile them with line sources:

int main()
{
    abc obj1;
    // Default constructor

    std::cout << "OBJ1 " << obj1.a << "..." << obj1.b << "\n";
    // OBJ1 0...0

    abc obj2;
    // Default constructor

    std::cout << "OBJ2 " << obj2.a << "..." << obj2.b << "\n";
    // OBJ2 0...0

    obj2 = 100;
    // Int constructor
    // Assignment operator (0, 0) = (100, 0)
    // Destructor Called

    std::cout << "OBJ2 " << obj2.a << "\n";
    // OBJ2 100

    return 0;
    // Destructor Called
    // Destructor Called
}

You mostly had it all, let us examine the surprises.

First surprise: even though obj2 changes value later abc obj2; will still call the default constructor at the point of declaration.

Second surprise: obj2 = 100 actually means obj2.operator=(abc(100));, that is:

  • Build a temporary (unnamed) abc from abc(100)
  • Assign it to obj2
  • Destroy the temporary before moving on to the next statement

Third surprise: destructors are called at the end of the scope, right before the closing bracket } (and yes, after the return). Since you are using system("pause") I assume you are on Windows => though luck they are called after you end the pause and thus your console Windows disappears in the blink of an eye at the moment they would have appeared. You can either launch the program from a more permanent console, or use an extra scope:

int main () {
  {
    // your code here
  }
  system("pause"); 
  return 0;
}

Tags:

C++