我使用以下代码收到编译错误:
main.cpp: In function âint main()â:
main.cpp:38: error: no matching function for call to âComplex::Complex(Complex)â
main.cpp:22: note: candidates are: Complex::Complex(Complex&)
main.cpp:15: note: Complex::Complex(double, double)
但是当我将复制构造函数的参数类型更改为const Complex&时,它可以工作。 我认为默认构造函数将使用2 Complex :: Complex(2.0,0.0)调用,然后将调用复制构造函数来创建一个带有Complex(2.0.0)的副本。不正确吗?
#include <iostream>
using namespace std;
class Complex {
double re;
double im;
public:
Complex(double re=0, double im=0);
Complex(Complex& c);
~Complex() {};
void print();
};
Complex::Complex(double re, double im)
{
cout << "Constructor called with " << re << " " << im << endl;
this->re = re;
this->im = im;
}
Complex::Complex(Complex &c)
{
cout << "Copy constructor called " << endl;
re = c.re;
im = c.im;
}
void Complex::print()
{
cout << "real = " << re << endl;
cout << "imaginary = " << im << endl;
}
int main()
{
Complex a = 2;
a.print();
Complex b = a;
b.print();
}
答案 0 :(得分:9)
写作时
Complex a = 2;
编译器不会直接调用Complex
构造函数,使用0作为构建a
的默认参数,而是考虑它是否可以“转换”2到Complex
。
要进行转换,它会找到您的Complex(re,im)
版本并且可以使用它,这要归功于默认值以及您没有声明构造函数explicit
的事实,但是它必须找到将此值转移到a
。
此“传输”的工具可以是复制构造函数。但是,可以使用Complex(re,im)
构建的复杂值是临时,并且由于C ++中的一些可疑原因,您不能将临时作为非const引用传递给函数。
因此,您的复制构造函数不能与临时版本一起使用,并且编译器卡住了,因为没有办法使用2初始化a
。
如果您声明您的复制构造函数而不是接受const
引用,则可以将临时文件传递给您的复制构造函数以初始化a
,因此一切都按预期工作。
可以使用语法a
直接初始化Complex a(2)
,在这种情况下,不需要使用复制构造函数。
另请注意,当您使用语法Complex a = ...
时可能会出现奇怪的情况,编译器必须检查使用复制构造函数是否合法,但一旦检查了合法性,就不允许调用它并使用而是直接初始化。换句话说,即使你需要声明你的拷贝构造函数接受一个const引用仍能编译,编译器实际上可能会跳过该部分并直接构建a
而不调用拷贝构造函数(即使拷贝构造函数 - 如在你的情况下 - 有副作用)。添加了这个看似疯狂的规则,以便能够在生成的代码中进行一些优化。
答案 1 :(得分:1)
C ++中的复制构造函数需要参数的const
部分才能获取const参数,如您所发现的那样。否则你没有创建一个可以接受const参数的复制构造函数,你创建了一个以非const Complex&
作为参数的复制构造函数。
答案 2 :(得分:1)
您始终使用&
创建复制构造,因此您无需复制整个对象以使其使用它。创建对象副本需要时间,引用它会更有效率。
在任何情况下,都需要在对象参数之前加上const
,以便保证复制构造函数不来更改输入对象。例如:
Complex::Complex(const Complex &c)
{
cout << "Copy constructor called " << endl;
re = c.re;
im = c.im;
c.im = 'something'; // This would not work
}
的问候,
丹尼斯M。
答案 3 :(得分:1)
此代码的问题在于您的复制构造函数是使用此签名定义的:
Complex::Complex(Complex &c)
这会将非const
引用作为参数,这可能不是您想要的。这意味着,例如,如果您尝试使用复制构造函数复制Complex
对象,则可以修改原始对象!
要解决此问题,请更改您的代码,以便Complex
引用const
:
Complex::Complex(const Complex &c)
更一般地说,复制构造函数和赋值运算符应该总是通过const引用接受它们的参数,除非你有充分的理由去思考。
我的代码中还有一些其他内容我应该指出。对于初学者,在这种情况下,您的复制构造函数不是必需的,因为它只是对所有字段进行直接复制。有一个称为“三规则”的经验法则,如果你有一个析构函数,你应该只有一个复制构造函数(然后你还应该有一个赋值运算符)。否则,编译器提供的默认函数应该足以满足您的需要。
此外,除非绝对必须,否则没有理由编写自己的Complex
课程。 <complex>
标头将complex<T>
定义为库类。