为什么C ++拷贝构造函数必须使用const对象?

时间:2013-06-06 07:59:31

标签: c++ copy-constructor

据我所知,当我们定义类的类复制构造函数时,Rule of three状态是必要的。我还注意到复制构造函数的参数通常是const,如下面的代码所示:

class ABC {
public:
    int a;
    int b;
    ABC(const ABC &other)
    { 
        a = other.a;
        b = other.b;
    }
}

我的问题是如果复制构造函数的参数不是const会发生什么:

class ABC
{
    public:
    int a;
    int b;
    ABC(ABC &other)
    { 
        a = other.a;
        b = other.b;
    }
}

据我所知,在某些情况下,如果复制构造函数的参数是const,那么第二个实现将失败。此外,如果复制构造函数的参数是const,则要复制的对象在此过程中不会更改其内容。但是,我注意到有些人仍然使用第二种实现而不是第一种实现。是否有任何理由认为第二种实施方式是首选的?

9 个答案:

答案 0 :(得分:23)

  • 从逻辑上讲,修改一个你只想制作副本的对象是没有意义的,虽然有时它可能有一些意义,比如你想存储这个时间的情况对象已被复制。但这可以使用存储此信息的mutable成员变量,甚至可以修改const对象(第二点将证明这种方法的合理性)

  • 您希望能够创建const对象的副本。但是如果你没有用const限定符传递你的参数,那么你就不能创建const对象的副本......

  • 您无法从临时引用创建副本,因为临时对象是rvalue,并且不能绑定引用非const。有关更详细的说明,我建议Herb Sutter's article on the matter

答案 1 :(得分:11)

你的类的任何消费者都会期望的最后一件事是一个复制构造函数,它改变了被复制的对象!因此,您应始终标记为const。

答案 2 :(得分:8)

这里可能需要const有两个原因:

  1. 确保您在制作副本时不会意外“损坏”原件 - 这是一件好事,因为您在制作副本时并不真的希望更改原始对象!
  2. 您可以传入除基本对象之外的其他内容 - 因为构造函数接受引用,如果它不是对象本身 - 例如表达式。
  3. 举例说明第二种情况:

     class ABC
        {
           public:
               int a;
               int b;
           ABC(const ABC &other)
           { 
             a = other.a;
             b = other.b;
           }
           ABC operator+(const ABC &other)
           {
               ABC res;
               res.a = a + other.a;
               res.b = b + other.b;
               return res;
           }
        }
    
      ...
      ABC A;
      a.a = 1;
      a.b = 2;
      ABC B(a+a);
    

    如果构造函数是ABC(ABC &other),则不会编译,因为a+a是ABC类型的临时对象。但如果它是ABC(const ABC &other),我们可以使用计算的临时结果,并仍然将其作为参考传递。

答案 3 :(得分:5)

正如其他几个答案所指出的那样,修改其参数的复制构造函数将是一个令人不快的意外。然而,这不是唯一的问题。复制构造函数有时与 temporaries 的参数一起使用。 (示例:从函数返回。)对临时值的非常量引用不会飞,如SO elsewhere所述。

答案 4 :(得分:3)

如果复制构造函数未将其参数指定为const,则此片段将无法编译。

const ABC foo;
ABC bar(foo);

答案 5 :(得分:1)

复制构造函数不应修改正在复制的对象,这就是const参数首选other的原因。两者都有效,但const是首选,因为它明确指出传入的对象不应被函数修改。

const仅供用户使用。它不存在于实际的可执行文件中。

答案 6 :(得分:0)

它不是"必须"在技​​术意义上。我们甚至在标准中都有这样的野兽,但it got deprecated。请按照链接进行推理。

我们期望的副本的语义是离开"模板"在所有方面提供一个完全等同的克隆,你很难说出原始的形式。

如果有所期待,你应该三思而后行。它会给用户带来惊喜并可能引入错误。和沮丧和噪音,只是尝试谷歌的auto_ptr'矢量只是为了看点数。

问题的其余部分可能是"我发誓不要在实施中触及原文,但想要一个不同的签名"。什么签名呢?让我们试试T和T&amp ;.

由于需要复制ctor才能使用,因此我们就会失败,而我们正在实施这一点。递归:参见递归

离开T&amp ;.这实际上适用于一系列案件。但是如果你的原始对象碰巧以常量形式存在,或者是暂时的,那就失败了。为什么在没有下雨的情况下阻碍那个明智的案例?

答案 7 :(得分:0)

复制构造函数需要const引用以避免无限循环。

class Cat
{
    //code
    Cat(Cat c)
    {
        //code
    }
    //code
};

调用复制构造函数时,它将创建对象c,即它将执行类似Cat c = sourceObj的操作。该语句应由副本分配运算符进行评估,但不是。这是因为c尚不存在,因此必须进行。因此,该任务将交给复制构造函数。

无限循环!

我很惊讶没有人提到这个明显的原因。

其他原因:

  • 您很少(永远不会)更改sourceObj
  • 非const绑定到临时对象。

答案 8 :(得分:-1)

除了复制构造函数不应修改源实例的基本假设之外,本文还详细阐述了使用 const 的实际技术原因:

http://www.geeksforgeeks.org/copy-constructor-argument-const/

即使我引用:

“...编译器创建的临时对象不能绑定到非const 引用......“