3

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?

Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • 7
    `A operator =(A obj){` takes a temporary copy, that one's destructor is called also. The signature should be `A& operator =(const & A obj){`. Also you're missing to `return *this;` – πάντα ῥεῖ Sep 05 '15 at 08:35
  • @πάνταῥεῖ why `const` ? –  Sep 05 '15 at 08:36
  • 1
    Because input won't be changed. – πάντα ῥεῖ Sep 05 '15 at 08:37
  • 5
    Urgent link: [The Definitive C++ Book Guide and List](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – Ivan Aksamentov - Drop Sep 05 '15 at 08:40
  • 1
    @Drop I'm in 2nd Semester of Software engineering and our teacher not teach us deeply.. I'm just thinking, working and unable to discuss with anyone. :) That's why I'm here.. :) and Books not provide everything to clear. :) Any how thanks. –  Sep 05 '15 at 08:42
  • @LetDoit The reason to use const is that it isn't usual to modify the right side of the assignment. – Rodolfo Sep 05 '15 at 08:44
  • This is worth looking at for some templates for copy and assignment : http://stackoverflow.com/search?q=rule+of+three – JCx Sep 05 '15 at 08:47
  • Are you sure that "Output" you provided is correct? It's missing something. What platform, compiler and compiler version you are using? – Ivan Aksamentov - Drop Sep 05 '15 at 08:58
  • @Drop DEV C++ 5.11 & Visual Studio 2015 –  Sep 05 '15 at 08:59
  • 1
    1) It is not compiled by VS2015. 2) Check it again after fixing the error and and thoroughly count constructors in a real output and one you've posted – Ivan Aksamentov - Drop Sep 05 '15 at 09:02
  • @Drop ohhhhhh Visual Studio 2015 Give different and logically acceptable output..!! So now what is the result of this difference? –  Sep 05 '15 at 09:04
  • 1
    It should not be different from GCC (the compiler behind Dev C++). The difference is that you are running different code somehow (by mistake). – Ivan Aksamentov - Drop Sep 05 '15 at 09:11

5 Answers5

3
     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.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
2

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;
}
kd84
  • 111
  • 3
2

[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
Marco A.
  • 43,032
  • 26
  • 132
  • 246
0

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.

skyking
  • 13,817
  • 1
  • 35
  • 57
  • I think the compiler shows only warnings in this case. You need to use the parameter "-Wall" to g++ show the warnings. – Rodolfo Sep 05 '15 at 08:51
0

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 !!