C ++默认构造函数

时间:2011-01-31 21:24:39

标签: c++ constructor

我使用以下代码收到编译错误:

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();
}

4 个答案:

答案 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>定义为库类。