当我们按值从方法返回对象时,为什么要调用复制构造函数

Why is the copy constructor called when we return an object from a method by value

本文关键字:为什么 调用 构造函数 复制 对象 我们 返回 方法      更新时间:2023-10-16

当我们按值从方法返回对象时,为什么要调用复制构造函数。请参阅我下面的代码,因为我从一个方法返回一个对象,而返回控件是点击复制构造函数然后返回。我不明白以下几点:
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;
 }

我会尝试同时回答所有问题。

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

Class c = fxn();

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

也就是说,允许实现根据C++11标准的第12/8.31段消除对复制或移动构造函数的一个或两个调用,即使这些构造函数有副作用,并直接在c:中构造函数的返回值

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

--在具有类返回类型的函数中的return语句中,当表达式是具有相同cv的非易失性自动对象(而不是函数或catch子句参数(不合格类型作为函数返回类型,可以通过构造来省略复制/移动操作自动对象直接输入函数的返回值

--[…]

--尚未绑定到引用(12.2(的临时类对象何时被复制/移动对于具有相同cv不合格类型的类对象,可以通过将临时对象直接构建到省略的复制/移动的目标中

--[…]

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

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

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

还要注意,构造函数的签名应该是:

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

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

更糟糕的是,上述操作会阻止从右值(如临时值(复制构造,因此以下代码不会编译:

Class foo() { return Class(); }

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

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

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

之所以调用复制构造函数,是因为您是通过值而不是引用进行调用的。因此,必须从当前对象实例化一个新对象,因为该对象的所有成员在返回的实例中都应该具有相同的值。因为否则,您将返回对象本身,该对象将通过引用返回。在这种情况下,修改引用对象也会更改原始对象。当按值返回时,通常不希望出现这种行为。
ClassA function (ClassA &str)
{
  return str;
}

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