为什么在按值从方法返回对象时调用复制构造函数

时间:2013-05-24 09:32:04

标签: c++ copy-constructor return-by-value

为什么在按值从方法返回对象时调用复制构造函数。请看我的下面的代码,因为我从方法返回一个对象,而返回控件正在击中复制构造函数然后返回。我不理解以下事情:
 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 个答案:

答案 0 :(得分:14)

我会尽快回答所有问题。

您正在观察的行为是由于在C ++中按值返回对象的方式。首先,临时对象是由函数返回的值复制构造的(或在C ++ 11中构造的移动构造)。然后,如果此返回值用于初始化另一个对象,例如:

Class c = fxn();

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

这就是说,一个实现允许 elide 一个(在你的具体情况下)或者这两个对C ++ 11 Standard的第12 / 8.31段的复制或移动构造函数的调用甚至虽然这些构造函数有副作用,但直接在c

中构造函数的返回值
  

当满足某些条件时,允许实现省略类的复制/移动构造   即使为复制/移动操作选择的构造函数和/或对象的析构函数也是如此   有副作用。在这种情况下,实现处理省略的复制/移动的源和目标   操作只是指向同一对象的两种不同方式,以及对该对象的破坏   发生在两个对象在没有优化的情况下被破坏的时间的晚期.122   在以下情况下允许复制/移动操作的省略,称为复制省略(其中   可以合并以消除多个副本)

     

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

     

- [...]

     

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

的目标中      

- [...]

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

  

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

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

另请注意,构造函数的签名应为:

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

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

更糟糕的是,上面会阻止rvalues(比如temporaries)的复制构造,所以下面的代码不能编译:

Class foo() { return Class(); }

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

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

对象str将在类型为ClassA的临时对象中进行复制构建,以供进一步使用。但是,由于优化,编译器可以省略它。

答案 3 :(得分:1)

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