147

为什么复制构造函数的参数必须通过引用传递?

10个答案10

重置为默认值
224

因为如果不是通过引用,则是通过价值。要做到这一点,您需要制作一个副本,为此,您需要调用复制构造函数。但要做到这一点,我们需要生成一个新值,所以我们调用复制构造函数,依此类推。。。

(您将拥有无限递归,因为“要制作副本,您需要制作副本”。)

4
  • 它不能作为实例的pass-by指针有什么原因吗? 评论 2010年4月21日20:52
  • 26
    然后它不再是一个复制构造函数,而是一个碰巧接受指针的常规旧构造函数。 评论 2010年4月21日21:01
  • 8
    @Barry当编译器试图通过调用对象o(其他对象)。但这仅在以下情况下有效对象具有接受另一个构造函数的构造函数对象通过值或引用。您已经知道为什么通过值传递不起作用,所以唯一的方法是通过引用或常量引用传递。如果您的“copy-constructor”将指针指向对象那么编译器的代码必须是对象o(&other_object)因此,从本质上说,你编写了一个满足编译器和用户期望的构造函数。 评论 2010年4月21日23:46
  • 我的编译器注意到的另一个很好的原因是,如果您有一个带有纯虚函数的基类,则无法通过值初始化此变量
    – 用户451498
    评论 2012年5月14日23:52
74

因为pass-by-value将调用复制构造函数:)

0
28

pass-by-reference的替代方法是pass-by-value。Pass-by-value实际上是Pass-by-copy。复制构造函数是制作副本所必需的。

如果您必须复制一个副本来调用复制构造函数,那将是一个难题。

(我认为编译器中会发生无限递归,而您实际上永远不会得到这样的程序。)

除此之外理性的原因,§12.8/3中的标准禁止:

的构造函数声明如果X类是第一个参数的类型为(可选cv-合格)X或没有其他参数或其他所有参数参数具有默认参数。

4
  • 编译器可以愉快地输出无限递归;我怀疑这不是一个特例。但是,如果使用非引用参数声明复制构造函数,则程序格式不正确。所以你说得对,它不应该编译。 评论 2010年4月21日20:09
  • @丹尼斯:我的意思是,如果你试图编译这样的程序,编译器会在生成代码时陷入困境。它不会生成递归函数,因为这个难题发生在调用程序调用函数之前。
    – 土豆水
    评论 2010年4月21日20:55
  • 无论哪种方式,无论你是否尝试使用该程序,它都是格式错误的。简单地定义构造函数就足以让编译器对您大喊大叫。 评论 2010年4月21日21:04
  • @丹尼斯:的确,尽管这只是一条规则。
    – 土豆水
    评论 2010年4月21日21:18
14

如果按值传递,它将无限递归

10

无论何时调用函数(例如:intf(carc))它接受除内置数据类型以外的参数(此处为car)复制调用方提供的实际对象的要求被调用函数参数中的变量。
例子:

汽车carobj;f(卡罗布);

也就是说,复制卡罗布c.

卡罗布需要复制到参数c在功能中(f).

为了实现复制,调用复制构造函数。

在这种情况下,函数(f)使用传递值或换句话说,函数调用(f)声明为通过值传递。

If函数(f)通过引用传递,则其声明为int f(car&c);

在这种情况下,
car carobj;f(卡罗布);

不需要复制构造函数。

在这种情况下,c成为的别名胡萝卜.

使用上述两个场景,为了清晰起见,我将其总结为:

  1. 如果函数被声明为将参数作为对象的值,则调用对象的复制构造函数。

  2. 如果函数声明将参数作为“引用传递”,则该参数将成为调用方提供的对象的别名。不需要复制构造函数!

现在的问题是为什么需要通过引用。如果复制构造函数接受引用,则接收变量将成为所提供对象的别名。因此,不需要复制构造函数(在本例中,调用自身)来复制调用方提供的对象中的值,以复制参数列表中构造函数的变量。

否则,如果复制构造函数将调用方提供的对象作为值,即通过值传递,则需要给定对象的复制构造函数;因此,为了将调用方提供的对象放入函数本身(在本例中为复制构造函数),我们需要调用复制构造函数,这只不过是在函数声明期间调用同一个函数。

这就是将引用传递给复制构造函数的原因。

1
  • 4
    虽然你是对的,但已经有四个答案解释了这一点,而且做得更清楚了。我不明白为什么你认为这个问题的第五个答案会有帮助。 评论 2012年3月13日0:32
7

有必要将对象作为引用传递,而不是按值传递,因为如果按值传递对象,则会使用复制构造函数构造其副本。这意味着复制构造函数将调用自身进行复制。这个过程将一直持续到编译器内存耗尽为止。

1

传递对象作为引用是非常重要的。如果对象作为值传递给Copy Constructor,则其复制构造函数将调用自身,以将实际参数复制到形式参数。因此,将启动对复制构造函数的无限调用链。这个过程将一直持续到系统内存耗尽。

因此,在复制构造函数中,参数应始终作为引用传递。

1

如果它不是通过引用传递的,那么它将通过值传递。如果参数是通过值传递的,则其复制构造函数将调用自身将实际参数复制到形参。这个过程将一直持续到系统内存耗尽为止。所以,我们应该通过引用传递它,这样复制构造函数就不会被调用。

1

复制构造函数定义了复制的含义,因此如果我们只传递一个对象(我们将传递该对象的副本),但要创建副本,我们需要一个复制构造函数,因此它会导致无限递归。

因此,复制构造函数必须有一个引用作为参数。

1

所有的答案都是正确的,但我想知道这些对像我这样通过示例学习的初学者是否有帮助。这里有一个例子。

以该代码为例:

#包括<iostream>#包括<cstring>使用命名空间标准;类字符串{私人:字符*s;int大小;公众:字符串(const char*str){大小=strlen(str);s=新字符[大小+1];strcpy(s,str);}~字符串(){删除[]s;}字符串(常量字符串&old_str){大小=old_str.size;s=新字符[大小+1];strcpy(s,old_str.s);}无效打印(){cout<<s<<endl;}无效更改(const char*str){删除[]s;大小=strlen(str);s=新字符[大小+1];strcpy(s,str);}};整型main(){字符串str1(“Hello World”);字符串str2=str1;str1.print();str2.print();str2.变更(“Namaste”);str1.print();str2.print();返回0;}

这是复制构造函数:

字符串(常量字符串&old_str){大小=old_str.size;s=新字符[大小+1];strcpy(s,旧_str.s);}

假设我们没有通过引用。即:

字符串(const字符串old_str){大小=old_str.size;s=新字符[大小+1];strcpy(s,old_str.s);}

看看intmain()函数。

字符串str2=str1

再次查看复制构造函数(无需引用)。

字符串(const字符串old_str)

这意味着:

字符串old_str=str1

它与main()函数中编写的内容基本相同。因此,它再次调用复制构造函数。在C++中调用复制构造函数的规则是当您遇到以下情况时:

ABC a;ABC b=a;

我希望这能有所帮助。

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.