【问题标题】:C++ has two copy constructors, both by reference and by value?C++ 有两个复制构造函数,都是按引用和按值?
【发布时间】:2011-12-08 19:57:59
【问题描述】:

考虑这段代码:

class complex{
        private:
                double re, im;
        public:
                complex(double _re, double _im):re(_re),im(_im){}
                complex(complex c):re(c.re),im(c.im){}
};

我已经知道复制构造函数complex(complex c)会导致无限递归。

但是,它应该只选择 const 引用复制构造函数 complex(const complex &c) 作为复制函数,因为如果没有明确指定,这是默认行为。排除其他所有内容,例如 complex(complex c)

这里为什么要应用传值的函数呢?或者两者都是拷贝构造函数,只是传值不能修改,用于传递给其他函数而不是其构造函数?

我认为在 Java 中是允许这样做的,因为它只会将对象复制到构造函数中。

【问题讨论】:

  • 您可能想添加我在上一个问题中给出的示例:complex(complex c)complex(const complex &c)
  • "这里为什么应用传值函数?"这个问题有什么遗漏吗?
  • @pmr 指的是我之前不久关闭的问题,因为有很多重复:stackoverflow.com/questions/8436467/…
  • @pmr 我认为 OP 正在询问重载是否有效:complex(complex c)complex(const complex &c) 如果有效,会发生什么。

标签: c++ copy-constructor


【解决方案1】:

复制构造函数必须采用以下形式之一:

T(T&);
T(const T&);

也就是说,构造函数是复制构造函数 只有如果它接受一个引用类类型的参数。根据定义,按值获取类类型的单个参数的构造函数不是复制构造函数(编辑)——事实上,正如 Cat Plus Plus 所指出的那样是非法的。

12.1 : 构造函数

10/类 X 的复制构造函数是第一个参数为 X& 或 const X& 类型的构造函数

然而,在这个标准之外,你对复制构造函数的理解是一个根本性的错误。考虑以下代码:

class Foo
{
public:
  Foo() {}
  Foo(Foo f) {/*...*/};
};

int main()
{
  Foo f1;
  Foo f2(f1);
}

f2 被构造时,f1 是按值传递的。为了评估构造函数调用的参数,必须复制f1。所以,你看这里有一个悖论。为了调用复制构造函数,您必须进行复制。为了进行复制,您必须调用复制构造函数...

上面必须使用按值参数调用构造函数,因为这就是参数的类型:它是Foo 按值。如果不是按值,则必须是按引用,如下所示:

Foo(Foo& rhs)
/* or like this: */ Foo(const Foo& rhs);

...或者它必须通过指针获取它,看起来像这样:

Foo(Foo* prhs)

...但是,在后一种情况下,这显然不是上面定义的复制构造函数。

【讨论】:

  • 他已经知道传值导致的无限递归了。
  • 啊,这是两个函数调用之间的歧义,因为无论是通过引用传递还是通过值传递,调用者都使用相同的函数调用。编译器会出错并按值构造函数调用副本。我的理解正确吗?
  • @Amumu 不,编译器不会使用“复制构造函数”编译代码,该“复制构造函数”按值获取参数(或者至少不应该,无论是否是不同的问题)。
  • @Seth 是的,编译器会错误地调用错误的函数,然后产生错误。我认为早期它会允许编译这个符号,正如你所说,即使是 VS 也允许。问题的重点是我想知道为什么。
  • @Amumu:不存在“按值复制”构造函数。
【解决方案2】:

complex(complex) 不是复制构造函数。它的格式不正确,应该被编译器拒绝。没有无限递归,因为你根本无法定义这样的构造函数。

如果类 X 的第一个参数的类型为 X&、const X&、volatile X& 或 const volatile X&,则类 X 的非模板构造函数是复制构造函数,并且要么没有其他参数,要么所有其他参数都有默认参数 ( 8.3.6)。

如果类 X 的第一个参数是类型(可选 cv 限定),则类 X 的构造函数声明是格式错误的 X 并且要么没有其他参数,要么所有其他参数都有默认参数。成员 函数模板永远不会被实例化以产生这样的构造函数签名。

【讨论】:

  • g++ 会拒绝吗?我知道 VS 没有。
  • 但是为什么不允许呢?在 Java 中,我记得是这样的。
  • @SethCarnegie 它执行 MSVS 2010 SP1
  • @Amumu 因为它没有意义,如果你这样做,那么要复制一个对象,你必须先复制一个对象。同样在 Java 中,您不会通过值传递事物,所有对象都是通过引用传递的,所以不,在 Java 中甚至不是这样。
  • @SethCarnegie 不,几乎所有内容都是参考。然而,这些引用是按值传递的。
【解决方案3】:

回答 Java 问题,因为给出了所有其他可能的解释:

//java code

public class Bar{
  private int foo;

  public Bar() { } // public no-args constructor
  public Bar(Bar b) { foo = b.foo; }  // copy constructor
}

Java 中的对象是引用,而不是 C++ 中的值对象。在 C++ 中,当您复制对象时,您会创建对象状态、内部变量等的副本。在 Java 中,它只是复制引用。对象的状态不会被复制,因此实际上不需要像在 C++ 中那样调用复制构造函数。

【讨论】:

  • Java的拷贝构造函数其实是Object.clone
  • 嗯,是的,但我认为 OP 的意思是这个。
  • 你是对的。我忘了 Java 是通过引用传递对象的。
猜你喜欢
  • 1970-01-01
  • 2016-03-05
  • 1970-01-01
  • 1970-01-01
  • 2012-02-12
  • 2023-03-31
  • 2015-11-24
  • 1970-01-01
  • 2013-06-12
相关资源
最近更新 更多