Reputation: 1374
I was going through Thinking in C++ and have some confusion regarding the behaviors of constructors in C++. Here is my sample code:
#include<iostream>
using namespace std;
class base
{
public:
int a;
/*Ctor 1*/ base() { cout<<" default"<<endl; }
/*Ctor 2*/ base(int a){ cout<<" base::int "<<endl; }
/*Ctor 3*/ base(const base& b) { cout<<" base::cc "<<endl; }
/*Asgn 1*/ base operator=(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
/*Asgn 2*/ /* base operator=(int b){
cout<<" base::assignment - int"<<endl;
return (base)b;
} */
};
int main()
{
base b;
int a = 10;
b = a;
system("PAUSE");
return 0;
}
Output :
Could anyone please explain me the output ? I expected just a call to
I am unable to understand why I get a call to assignment operator and copy constructor other object being "int" type. If I uncomment "Asgn 2' I get a call to it rather that Asgn 1 which is understandable.
If I am getting a call to copy constructor (which always take object reference as its parameter), is it because compiler casts int to base type?
Upvotes: 0
Views: 444
Reputation: 59997
The output
default
base::int
base::assignment - base
base::cc
Comes about as follows:
base b;
Here create a b
- This will use the default constructor
int a = 10;
b = a;
We have an assignment - the only one available takes a value of type base
- so the compiler scratches its head and say "ah-ha" got a version of a constructor that can create an object of type base
from an int
. We can use that.
So you get the output
cout<<" base::int "<<endl;
Now the compiler can use the assignment operator. The parameter is an object of type base
but as it is temporary this does not need to be called (see http://en.cppreference.com/w/cpp/language/copy_elision), The assignment operator then outputs
cout<<" base::assignment - base"<<endl;
But the assignment returns the value not as a reference - so it need to copy this return value into b
- thus calling the copy constructor. Hence
cout<<" base::cc "<<endl;
Upvotes: 2
Reputation: 8270
First of all, base(int a)
is a converting constructor as
it takes one argument
it doesn't use the explicit
keyword
A converting constructor can be used for implicit conversions:
void foo(base b);
void bar() {
foo(3);
}
Here the int
argument will be implicitly converted to base
type with the converting constructor.
Because by value arguments (arguments passed by reference) are copied, the copy constructor is officially called; but here, the source of the copy is a temporary object, the implicitly created object using the int
constructor. So the compiler is allowed to fuse the temporary and the parameter, directly constructing the target object. This optimisation is optional, and the compiler must still verify that the copy constructor could be called: that it is declared and accessible here (public).
Because the optimisation is very simple, almost all (or all?) compilers do it; many compilers do it at even the less aggressive optimisation levels (where most optimisations are disabled).
You declare the assignment operator as taking by a parameter by value and returning a copy (not a reference), which is quite rare (but not illegal):
/*Asgn 1*/ base operator=(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
This means that the copy constructor is needed to pass an argument to the assignment operator, and also for return
instruction.
Please note that the fact it is an operator is irrelevant, you could have called it assign
:
/*Asgn 1*/ base assign(base b){
cout<<" base::assignment - base"<<endl;
return b;
}
and call it normally:
base a,b;
a.assign(b);
b.assign(base());
b.assign(base(2));
b.assign(3);
a.assign(b)
will call the copy constructor to create the parameter of assign
.
base()
creates a temporary object using the default constructor, and base(2)
creates one using the int
constructor (when you explicitly create a temporary, it does not matter whether the constructor is a converting constructor). Then you can assign
on the created temporary. The copy construction is avoided by the compiler by constructing directly the parameter.
In b.assign(3)
, the creation of the temporary is implicit, and the fact the constructor is a converting constructor is relevant.
The return statement creates another copy; the usual idiom for operator=
is:
type& type::operator= (const type &source) {
copy stuff
return *this;
}
A reference is bound to the target object, and no redundant copying occurs.
Upvotes: 1