lostbits
lostbits

Reputation: 1054

copy constructor called twice

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

Answers (4)

PypeBros
PypeBros

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,

  1. you must not return a reference on something you constructed locally on this stack frame (that is, a local variable or a function argument)
  2. imvho, if you're just updating the content referenced by the caller, then it make no sense to return a reference to that object, and it would be equally simple to have a "setter-like" API where you have 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

Joseph Mansfield
Joseph Mansfield

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:

  1. const

    Object is copied - this is the object being passed into the function as an argument.

  2. fnc1(able x)

    Execution of fnc1.

  3. const

    Object is copied again - this is the object being returned from the function.

  4. ~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.

  5. ~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

Andy Prowl
Andy Prowl

Reputation: 126432

In this function:

able fnc1(able x) { ... } 

You are:

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

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

Mats Petersson
Mats Petersson

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

Related Questions