为什么复制构造函数应该在 C++ 中通过引用接受其参数?

Why should the copy constructor accept its parameter by reference in C++?

提问人:Jony 提问时间:4/22/2010 最后编辑:pkthapaJony 更新时间:4/24/2023 访问量:88659

问:

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

C++ 复制 复制构造函数

评论


答:

219赞 GManNickG 4/22/2010 #1

因为如果它不是通过引用,它就是通过值。为此,请创建一个副本,为此,请调用 copy 构造函数。但要做到这一点,我们需要创建一个新值,所以我们调用复制构造函数,依此类推......

(你会有无限递归,因为“要制作副本,你需要制作副本”。

评论

0赞 Barry Wark 4/22/2010
有没有理由不能通过指针传递到实例?
24赞 Dennis Zickefoose 4/22/2010
然后,它就不再是一个复制构造函数,而只是一个碰巧接受指针的常规旧构造函数。
8赞 wilhelmtell 4/22/2010
@Barry,当编译器尝试通过调用 来自行创建对象的副本时,通常会实现复制构造函数。但是,这仅在具有按值或引用获取另一个构造函数时才有效。您已经知道为什么按值传递不起作用,因此唯一的方法是通过引用或常量引用传递。如果你的“复制构造函数”将指针指向一个,那么编译器的代码必须是 .因此,从本质上讲,您编写了一个满足编译器和用户期望的构造函数。object o(other_object)objectobjectobjectobject o(&other_object)
0赞 5/15/2012
我的编译器指出的另一个很好的理由是,如果你有一个具有纯虚函数的基类,那么这个变量的值初始化是不可能的
74赞 Brian Roach 4/22/2010 #2

因为按值传递会调用复制构造函数:)

14赞 Romain Hippeau 4/22/2010 #3

如果你按值传入它,它将是无限递归的

27赞 Potatoswatter 4/22/2010 #4

按引用传递的替代方法是按值传递。按值传递实际上是按副本传递。需要复制构造函数来制作副本。

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

(我认为无限递归会发生在编译器中,你永远不会真正得到这样的程序。

除了合理原因外,§12.8/3 中的标准也禁止这样做:

构造函数的声明 类 X 的格式不正确,如果它的第一个 参数的类型(可选 cv- 合格)X,要么没有 其他参数或其他所有其他参数 parameters 具有默认参数。

评论

0赞 Dennis Zickefoose 4/22/2010
编译器可以愉快地吐出无限递归;我怀疑这不是一个特例。但是,如果使用非引用参数声明复制构造函数,则程序的格式不正确。所以你是对的,它不应该编译。
0赞 Potatoswatter 4/22/2010
@Dennis:我的意思是,如果你试图编译这样的程序,编译器在尝试生成代码时会卡住。它不会生成递归函数,因为难题发生在函数调用之前,在调用方中。
0赞 Dennis Zickefoose 4/22/2010
无论哪种方式,无论您是否尝试使用它,该程序的格式都不正确。仅仅定义构造函数就足以让编译器对你大喊大叫。
0赞 Potatoswatter 4/22/2010
@Dennis:确实如此,尽管这只是一个规则。
10赞 Sriraman 3/13/2012 #5

每当您调用函数时(例如:int f(car c)) 它采用内置数据类型以外的参数(此处为 car) 复制调用方提供的实际对象的要求 添加到被调用函数参数中的变量。
例:

car carobj; f(carobj);

也就是说,复制到 .carobjc

carobj需要复制到函数中的参数。cf

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

在这种情况下,使用传递值调用的函数,或者换句话说,函数被声明为接受传递值。ff

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

在本例中,
car carobj; f(carobj);

不需要复制构造函数。

在本例中,成为 的别名。ccarobj

使用上述 2 种情况,为了您的清楚起见,我将它们总结为:

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

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

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

否则,如果复制构造函数将调用方提供的对象作为值,即按值传递,则它需要给定对象的复制构造函数;因此,为了将提供的对象从调用者获取到我们的函数本身(在本例中为 Copy 构造函数),我们需要调用 Copy 构造函数,这只不过是在函数声明中调用相同的函数。

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

评论

4赞 Ben Voigt 3/13/2012
虽然你是对的,但已经有四个答案解释了这一点,而且做得更清楚。我不明白为什么你认为这个问题的第五个答案会有所帮助。
1赞 Dalbir Singh 1/29/2014 #6

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

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

1赞 ishaang 3/10/2014 #7

如果它不通过引用传递,那么它将通过值传递。如果参数按值传递,则其复制构造函数将调用自身以将实际参数复制到形式参数。此过程将持续到系统内存不足为止。 因此,我们应该通过引用传递它,以便复制构造函数不会被调用。

7赞 Anand Tripathi 8/29/2015 #8

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

0赞 Abhilekh Gautam 8/12/2021 #9

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

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

0赞 miro plesko 4/24/2023 #10

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

以以下代码为例:

#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
    char *s;
    int size;
public:
    String(const char *str)
    {

        size=strlen(str);
        s=new char[size+1];
        strcpy(s,str);
    }
    ~String()
    {
        delete[] s;
    }

    String(const String& old_str)
    {
        size=old_str.size;
        s=new char[size+1];
        strcpy(s,old_str.s);

    }
    void print()
    {
        cout<<s<<endl;
    }
    void change(const char *str)
    {
        delete [] s;
        size=strlen(str);
        s=new char[size+1];
        strcpy(s,str);
    }
};
int main()
{
    String str1("Hello World");
    String str2=str1;

    str1.print();
    str2.print();

    str2.change("Namaste");
    str1.print();
    str2.print();
    return 0;
}

这是复制构造函数:

String(const String& old_str)
        {
            size=old_str.size;
            s=new char[size+1];
            strcpy(s,old_str.s);
    
        }

想象一下,我们不通过引用。结婚

String(const String old_str)
        {
            size=old_str.size;
            s=new char[size+1];
            strcpy(s,old_str.s);
    
        }

查看 int main() 函数。

String str2=str1

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

String(const String old_str)

这意味着:

String old_str=str1

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

ABC a;
ABC b=a;

我希望这会有所帮助。