user5028722
user5028722

Reputation:

Why Destructor called Three Time?

I'm working on RVO/Copy-Constructor/Destructor and checking code by randomly. I'm little confused here why destructor called three time..??

#include <iostream>
using namespace std;
class A{
    public:
        A(){
            cout << "Simple Constructor" << endl;
        }
        A(const A& obj){
            cout << "Copy Constructor " << endl;
        }
        A operator =(A obj){
            cout << "Assignment Operator" << endl;
        }
        ~A(){
            cout << "Destructor " << endl;
        }       
};
A fun(A &obj){
    cout << "Fun" << endl;
    return obj;
}
int main(){
    A obj;
    obj=fun(obj);
    cout << "End" << endl;
    return 0;
}

Output:

Simple Constructor // ok
Fun // ok
Copy Constructor // ok for =
Assignment Operator // ok
Destructor // ok for =
Destructor // why here destructor called?
End // ok
Destructor // ok for main

I was expecting Destructor to be called two times.

One for (=) operator's Object.

Second one for int main()'s object.

Why is it being called the third time? And how?

Upvotes: 3

Views: 265

Answers (5)

Marco A.
Marco A.

Reputation: 43662

[stmt.return]/p2 from N4527

Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.

The extra-destructor is called to deallocate an uninitialized object - Undefined Behavior

That is also the reason why clang/MSVC don't accept your code in the first place. Gcc does though with a warning.


Detailed explanation:

You're not returning any object though you said operator= returns one by value. This means skipping initialization and treating one as a fully-fledged object.

Here's how things should go with gcc (the only compiler which actually accepts your code and yields your output):

Simple Constructor // Construct obj in main
Fun // Calls the function
Copy Constructor // To construct operator='s argument directly (RVO elision here)
Assignment Operator // Assignment operator
(assignment doesn't return anything and gcc accepts it - so nop here)
Destructor // Destroy operator='s temporary (the parameter)
Destructor // Destroy the UNINITIALIZED object allocated for the result of operator=
End
Destructor // obj in main

Upvotes: 2

Prem Yadav
Prem Yadav

Reputation: 1

The possible reasons for three destructor calls could be,

  1. you are passing local object to assignment operator so it get deleted when function execution stop
  2. obj = fun(obj), here you are assigning a new object to "obj", so the old one has to deleted
  3. Last one is the "obj" itself get deleted when main function ends.

Hope it will do !!

Upvotes: 0

David Hammen
David Hammen

Reputation: 33126

     A operator =(A obj){
        cout << "Assignment Operator" << endl;
    }

You declared the function A::operator= as having a return type (an instance of A in this case), but the implementation has no return statement. This is undefined behavior.

You are essentially asking about the response to undefined behavior. The answer is "anything goes". In your case, with your compiler and your system, you are getting one more call to the destructor than to the constructors. You're lucky that you didn't your program didn't create nasal demons or erase your hard drive.

The solution is simple: Don't invoke undefined behavior. The canonical way to write a copy assignment operator is to have the return type be a reference to the class and to return *this:

     A& operator =(A obj){
        cout << "Assignment Operator" << endl;
        return *this;
    }

With this correction, you'll see that calls to the various constructors and calls to the destructor balance.

Suppose you instead used a non-standard copy assignment operator (but with a properly written return statement):

     A operator =(A obj){
        cout << "Assignment Operator" << endl;
        return *this;
    }

Once again you would see that calls to the constructors and to the destructor balance. How many calls you will see depends on your compiler and on the optimization level.

Upvotes: 3

kd84
kd84

Reputation: 121

You are passing obj by value and also returning by value in following function:

A operator =(A obj){
    cout << "Assignment Operator" << endl;
    return *this;
}

To avoid additional copy, you should change your function to:

A& operator =(const A& obj){
    cout << "Assignment Operator" << endl;
    return *this;
}

Upvotes: 2

skyking
skyking

Reputation: 14400

It look's like it's because you don't return an object from the assignment operator, but the caller has to destroy the supposedly returned value. What surprises me is that the compiler will allow this (or that the language allows it when the returned object is supposed to be constructed).

So the objects that are destroyed are:

  1. The supposed returned temporary object from assignment operator
  2. The temporary object sent to the assignment operator
  3. The original object defined in main

The objects constructed are:

  1. The temporary object sent to the assignment operator
  2. The original object defined in main

Either defining the assignment operator as returning void or actually return obj or *this will remove this discrepancy.

Upvotes: 0

Related Questions