0

Given this class:

class Baz
{
public:
    Baz() : Baz(0) {}
    Baz(int i) { _i = i; }
    Baz(Baz const &b) { _i = b._i * 10; }
    ~Baz() { }
private:
    int _i;
};

If I debug and step through this code:

Baz a = 4;

The Baz constructor that takes an int is called, as expected, and I get a Baz with _i of 4. That's good.

But if I do this:

Baz b;
b = 4;

The first line calls the default constructor, as expected. The second line calls the int constructor and then the destructor.

My first expectation was that the second line of the second example would simply call the int constructor in assigning to b. I didn't expect the destructor to be called, but if it's first converting the integer 4 to a Baz before assignment, it would make sense to destroy it afterward. But then I'd expect the copy constructor to be called when assigning that temporary value to b. The Baz that is destroyed has the value 4 for _i, so it's not the object created on the first line.

What's going on here exactly? Why the difference between these two scenarios?

Tom Hamming
  • 10,577
  • 11
  • 71
  • 145
  • It seems you are not familiar with the assignment operator. I recommend reading about the Rule of Three (or Rule of Five), which helps a lot here. Links: [Rule of Three on Wikipedia](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)), [Rule of Three on Stack Overflow](https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three) – Fabio says Reinstate Monica Jul 09 '20 at 00:33

4 Answers4

3

This line:

b = 4;

first calls the int constructor of Baz, to construct a temporary Baz with the value 4. This temporary is destroyed at the semi-colon.

The assignment does not call the copy-constructor, but actually calls the copy-assignment operator, which has the signature Baz& operator=(Baz const &);.

cigien
  • 57,834
  • 11
  • 73
  • 112
3

This line:

Baz a = 4;

Is equivelent to:

Baz a(4);   // It simply constructs the object.
            // Note the `=` is not an assignment when
            // used in a declaration like this. It is
            // simply short hand (syntactic sugar) for
            // a single argument constructor.

While these two lines:

Baz b;      // Default construct thus setting _i to zero.
b = 4;      // This **IS** an assignment and needs an assignment
            // operator (or there is a compiler error).

            // Note there is no assignment operator that takes
            //      an integer. But there is a default assignment
            //      operator that takes a Baz by const reference.
            //
            //      This is auto generated by the compiler.
            //      See rule of 5
            //         Baz& operator=(Baz const& copy): _i(copy._i){return *this)
            //
            // So the compiler must convert the integer to Baz before 
            // an assignment is allowed. The compiler is allowed to 
            // create an Object using a one parameter constructor so
            // that line is equivalent too:

  // Equiv
  b = Baz(4);  // So this creates a temporary Baz object here.
               // Then the assignment operator is called.
               // Then at the end of the statement the temporary
               // object must be destroyed so we call the destructor  
               // on the temporary.
Martin York
  • 257,169
  • 86
  • 333
  • 562
2

The statement Baz a = 4; is just syntax sugar for Baz a(4);, so it is calling the Baz(int) converting constructor directly.

Baz does not have an operator=(int) defined, but it has an operator=(const Baz&) instead (which is being generated by the compiler in your example). And Baz has a Baz(int) constructor. And a const Baz& parameter can bind to an rvalue, and there is only 1 implict conversion from an int to a Baz. So, the compiler is able to treat the statement b = 4; as b = Baz(4);, ie it creates a temporary Baz object and passes it to operator=. The temporary is destroyed at the end of the statement that creates it (ie, at the ;).

So, it is the int constructor and destructor of that temporary Baz object that you are seeing.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1

A constructor can only be used to create a new object.

b = 4; doesn't create b, so it can't call a constructor on b. Instead it creates a temporary Baz instance using the Baz(int i) constructor, and then assigns the temporary to b (using operator=, which is generated by the compiler in your case).

The temporary is destroyed at the end of the full expression (i.e. when control reaches ;), that's why you see a destructor call.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207