11

为什么当我们从方法中按值返回对象时会调用复制构造函数。请看我下面的代码,因为我从一个方法返回一个对象,而返回控制是点击复制构造函数然后返回。我不明白以下事情:
1)为什么它调用复制构造函数。
2)哪个对象隐式传递给复制构造函数,
3)复制构造函数将内容复制到哪个对象,
4)在返回时复制对象内容的必要性是什么。所以请帮忙。

#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;
 }
4

4 回答 4

19

我会尝试一次回答所有问题。

您观察到的行为是由于在 C++ 中按值返回对象的方式。首先,从函数返回的值复制构造一个临时对象(或在 C++11 中移动构造)。那么,如果这个返回值被用来初始化另一个对象,比如在:

Class c = fxn();

该对象c是从该临时对象复制构造的(或在 C++11 中移动构造的)。

这就是说,即使这些构造函数有副作用,也允许实现根据 C++11 标准的第 12/8.31 段省略一个(在您的具体情况下)或两个对复制或移动构造函数的调用,并构造函数的返回值直接在c

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本应被删除的较晚时间。在没有优化的情况下销毁 。122 这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以结合起来消除多个副本)

— 在具有类返回类型的函数的语句中,当表达式是具有与函数返回类型相同的 cv 非限定类型return的非易失性自动对象(函数或 catch 子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值中,可以省略复制/移动操作

— [...]

— 当尚未绑定到引用 (12.2) 的临时类对象将被复制/移动到具有相同 cv-unqualified 类型的类对象时,可以通过将临时对象直接构造到省略复制/移动的目标

— [...]

我之所以写,在您的特定情况下,只有一个对复制或移动构造函数的调用可以省略,是上面标准引用的第一个项目符号中的粗体句子:

[...](函数或 catch 子句参数除外)[...]

如果您要返回函数的参数,则会禁止复制省略。

另请注意,您的构造函数的签名应该是:

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

您没有理由const在复制构造函数中接受对 non- 的左值引用,因为您不会修改要从中复制的对象。

更糟糕的是,上述内容会阻止从右值(如临时对象)进行复制构造,因此以下代码将无法编译:

Class foo() { return Class(); }

即使允许实现删除副本,仍然应该存在可行且可访问的副本构造函数(或移动构造函数,如果我们正在谈论移动)。

于 2013-05-24T09:36:14.903 回答
3

该函数返回一个对象。因此,该对象必须存在。因此,该对象必须从某个地方创建。显然,这意味着应使用其构造函数之一。问题是,哪一个?

由于您选择了return str;,因此这是用于创建它的指令。你怎么能使用该返回指令来创建和返回一个对象而不使用复制构造函数?很明显,您需要使用str来初始化返回值,因此您不会使用其他选项(无参数构造函数)。

于 2013-05-24T09:37:01.163 回答
2
ClassA function (ClassA &str)
{
  return str;
}

对象str将复制构造到具有类型的临时对象中以ClassA供进一步使用。但是编译器可以由于优化而忽略它。

于 2013-05-24T09:36:22.963 回答
2

调用复制构造函数是因为您按值而不是按引用调用。因此,必须从当前对象实例化一个新对象,因为该对象的所有成员在返回的实例中都应该具有相同的值。因为否则您将返回它自己的对象,该对象将通过引用返回。在这种情况下,修改参考对象也会改变原始对象。这通常不是按值返回时想要的行为。

于 2013-05-24T09:38:53.810 回答