Reputation: 1054
Compiler gcc 4.5.3 (cygwin)
I'm trying to determine under what conditions the copy constructor is called for an argument, and I would like to discover a way to pass an argument that does not require the copy constructor to be called. I constructed the following test code to explore this issue.
In the following code the copy constructor is called twice for fnc1(). Any reason why it should be called more than once?
Is there any way to not have the copy constructor called?
# include <iostream>
using namespace std;
class able {
public:
long x;
able(): x(1) {}
able(const able&) {cout << " const "; }
~able() { cout << " ~able" << endl; }
};
able fnc1(able x) { cout << "fnc1(able x)" ; return x; }
able fnc2(able& x) { cout << "fnc2(able& x)" ; return x; }
able fnc3(const able& x) { cout << "fnc3(const able& x)" ; return x; }
able fnc4(able const & x) { cout << "fnc4(able const & x)" ; return x; }
able fnc5(able* x) { cout << "fnc4(able* x)" ; return *x; }
int main(int argc, char** argv) {
able* x = new able();
fnc1(*x);
fnc2(*x);
fnc3(*x);
fnc4(*x);
fnc5(x);
cout << "test fini" << endl;
return 0;
}
output
const fnc1(able x) const ~able
| | |
| | o first destrucor
| |
| o second call
o first call
~able
|
o second destructor
fnc2(able& x) const ~able
fnc3(const able& x) const ~able
fnc4(able const & x) const ~able
fnc4(able* x) const ~able
test fini
Upvotes: 0
Views: 1110
Reputation: 2646
It sounds like you want the responsibility of the 'able' object shifted to the caller. In that case, you'd have a prototype like void fn1(able &x)
, and it's up to the caller to create a private copy of the x
object if it doesn't want its preciousss to be messed up.
One copy for the parameter, one copy for the return value, as others said.
As long as you return an new object from your function, you need a constructor to build that object. If you don't want a new object at that point, then your function should return a pointer or a reference to that other object. However,
void
as return type.The only reason I can see for not following #2 would be that you want to chain functions, i.e. you value obj->fn1()->fn2()->fn3()
over other programming patterns. In that case, I'd suggest that you receive and return pointers to able
objects, and live with the fact that fnx()
use obj->m
rather than obj.m
to access members.
Upvotes: 0
Reputation: 110658
You are passing the able
object into the function by value and then returning it by value. Each of these involves a copy and will use your copy constructor. First it is copied into the function with fnc1(*x);
. Then that copy is copied out of the function with return x;
.
As for the order of your output, what you're witnessing is:
const
Object is copied - this is the object being passed into the function as an argument.
fnc1(able x)
Execution of fnc1
.
const
Object is copied again - this is the object being returned from the function.
~able
Destructor is called - this is the copy created in passing the argument being destroyed because you've reached the end of the functions scope.
~able
Destructor is called - this is the temporary object that was returned from the function being destroyed when the line fnc1(*x);
completes.
The second copy, caused by return x;
, may be elided by the compiler (even if it has some side effects):
in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value
Upvotes: 6
Reputation: 126432
In this function:
able fnc1(able x) { ... }
You are:
Taking the input argument by value. This means a copy of it will be created to initialize x
. This is the reason for the first invocation of the copy constructor.
Returning an object of type able
by value: this means a temporary will be constructed which is a copy of the object you are returning, although this last call to the copy constructor (and then to the destructor, of course) may be elided by the compiler under the (Named) Return Value Optimization, or (N)RVO. This is the reason for the second invocation of the copy constructor.
Hence, the two calls to the copy constructor that you are seeing.
Also notice, that your program leaks memory. You are allocating an object with new
, and you are never deallocating it through a corresponding call to delete
.
Upvotes: 2
Reputation: 129344
This function:
able fnc1(able x) { ... }
will require that a copy is made of the object - That's part of the contract when you use a class without reference. That allows fnc1() to "mess" with the object and the original object passed in to remain the same - which is sometimes exactly what you want.
Upvotes: 0