Reputation: 101
Consider the following scenario
class Integer
{
long long n;
public:
Integer(long long i):n(i){}
Integer(){cout<<"constructor";}
void print()
{
cout<<n<<endl;
}
Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;}
Integer operator+(Integer b);
};
Integer Integer:: operator+(Integer b)
{
this->n = this->n + b.n;
return *this;
}
int main()
{
// your code goes here
Integer a(5);
Integer b(6);
Integer c(a+b);//line 1
return 0;
}
If a+b
was temporary then i understand that copy constructor would not be called. But a+b
does not return a temporary.
The output i get is
copy constructor 6 //this is because Integer object is passed as value to operator+
copy constructor -5232903157125162015 //this is when object is returned by value from operator+
I think there should be one more call when a+b
is used to initialize c
. Most of the related questions have to do with return value optimization but i
can't relate RVO to this.
Upvotes: 2
Views: 95
Reputation: 6014
To answer your question, the copy constructor never copies anything and the +
operator isn't overloaded correctly. Additionally, the default constructor leaves the Integer
class with an uninitialized member, which may cause undefined behaviors (not 100% on that one) which may be causing some issues.
So what happens is, because the copy constructors never actually copy a value to the n
member of *this
, the default constructor is called, and std::cout displays whatever is at the location of n
of 'a.n'; which, if uninitialized, could be anything.
This website from Caltech is good, if imperfect, reference for overloading operators.
Try this.
#include <iostream>
using namespace std;
class Integer{
long long n;
public:
Integer(long long i):n(i){}
//Integer(){cout<<"cosntructor";} //This creates an unitialized instance of integer.
Integer(){// Do this instead.
n = 0;
cout << "Constructor" << endl;
}
void print(){
cout<<n<<endl;
}
//Integer(const Integer &a){cout<<"copy constructor"<<" "<<a.n<<endl;} // This isn't a copy contructor.
Integer(const Integer &a){ //This is a copy contructor.
this->n = a.n;
cout << "Copy constructor" << " " << a.n << endl;
}
Integer& operator+=(const Integer &);
Integer operator+(const Integer &);
};
Integer& Integer::operator+=(const Integer &b){ //Always overload incrementors/decrementors first, makes life easier.
this->n = this->n + b.n;
return *this;
}
Integer Integer:: operator+(const Integer &b){
return Integer(*this)+=b.n; //Notice the use of overloaded incrementor inside the '+' operator.
}
int main(){
// your code goes here
Integer a(5);
Integer b(6);
Integer c(a+b);//line 1
return 0;
}
Upvotes: 1
Reputation: 30489
You are witnessing the copy ellission of the constructor.
When a nameless temporary, not bound to any references, would be moved or copied into an object of the same type (ignoring top-level cv-qualification), the copy/move is omitted. When that temporary is constructed, it is constructed directly in the storage where it would otherwise be moved or copied to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".
To view the expected output (not eliding of constructor) use -fno-elide-constructors
option in gcc
.
EDIT
As mentioned in my comment on your question Integer:: operator+
is not quite right. Moreover as mentioned in other answers, your copy constructor doesn't do the initialization of member and thus causes undefined behaviuor.
Upvotes: 1
Reputation: 148880
You say a+b
does not return a temporary. Wrong. a+b
(even if it changes a
which will certainly mislead an future reader...) returns a temporary copy of a
because it is declared to return an Integer
and not a reference (Integer&
).
So here is what happens:
Integer a(5); // creates an integer from int 5
Integer b(6); // creates an integer from int 6
Integer c(a+b); /* first creates a temp using copy ctor from b
updates a
creates a temp copy of the result
should create an integer by copy of the result (elided)
destroys the temporary created from b
*/
return 0; // destroys c, b, a
BTW: you had forgotten to initialize n
in your copy ctor - it should be:
Integer(const Integer &a):n(a.n) {cout<<"copy constructor"<<" "<<a.n<<endl;}
Upvotes: 2
Reputation: 13698
It has a correct number of copy ctor calls:
1st is for b
because operator+(Integer b)
accepts Integer
by value.
2nd is for c
created by operator+
result.
By the standard there could be a 3rd call: return *this
might have created a temporary which would later be copied to c
. But in reality it is always elided. You can turn off this elision in gcc&clang and you can't turn it off in MSVC.
Upvotes: 0