Why is the copy constructor called when we return

2020-03-19 03:55发布

问题:

why copy constructor is called when we return an object from a method by value. please see my below code in that i am returning an object from a method while returning control is hitting the copy constructor and then returning. i am not understood following things:
1) why it is calling copy constructor.
2)which object is passing implicitly to copy constructor,
3)to which object copy constructor will copy the content,
4)what is the necessity of copying the object content while returning. so plz help.

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class ClassA
{
   int a, b;
     public:
   ClassA()
   {
     a = 10;
     b = 20;
   }
   ClassA(ClassA &obj)
   {
    cout << "copy constructor called" << endl;
   }
 };

 ClassA function (ClassA &str)
 {
  return str;
 }

 int main ()
 {
   ClassA str;
   function(str);
   //function(str);
   return 0;
 }

回答1:

The copy constructor is called because you call by value not by reference. Therefore a new object must be instantiated from your current object since all members of the object should have the same value in the returned instance. Because otherwise you would be returning the object it self, which would be returning by reference. In this case modifying the reference object would change the original as well. This is generally not a behavior one wants when returning by value.



回答2:

I'll try to answer all the questions at once.

The behavior you are observing is due to the way returning an object by value works in C++. First, a temporary object is copy-constructed (or move-constructed, in C++11) from the value returned by your function. Then, if this return value is used to initialize another object, such as in:

Class c = fxn();

The object c is copy-constructed (or move-constructed, in C++11), from that temporary.

This said, an implementation is allowed to elide one (in your concrete case) or both of these calls to the copy or move constructor per paragraph 12/8.31 of the C++11 Standard even though those constructors have side effects, and construct the function's return value directly in c:

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.122 This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

— 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

— [...]

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

— [...]

The reason why I wrote that in your specific case only one of the calls to the copy or move constructor can be elided is the sentence in bold in the first bullet of the Standard quote above:

[...] (other than a function or catch-clause parameter) [...]

If you are returning a parameter of your function, copy elision is inhibited.

Also notice, that the signature of your constructor should be:

Class(Class const& c)
//          ^^^^^

There is no reason for you to accept an lvalue reference to non-const in the copy constructor, since you are not going to modify the object you are copying from.

Even worse, the above would prevent copy construction from rvalues (like temporaries), so the following code would not compile:

Class foo() { return Class(); }

Even though an implementation is allowed to elide the copy, a viable and accessible copy constructor (or move constructor, if we're talking about a move) should still be present.



回答3:

The function returns an object. Hence, that object must exist. Hence, that object must be created from somewhere. Clearly this means that one of its constructors shall be used. The question is, which one?

Since you chose to return str;, this is the instruction that shall be used to create it. How else could you use that return instruction to create and return an object and yet not use the copy constructor? It is clear that you need to use str to initialize the returned value, so you're not going to use the other option (the parameterless constructor).



回答4:

ClassA function (ClassA &str)
{
  return str;
}

object str will copy-constructored in a temporary object with type ClassA for further usage. However compiler can omit it due to optimization.