开发者

C++ default constructors

I get a compile error with the following code:

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)

But when I change the argument type of the copy constructor to const Complex&, it works. I was thinking that the default constructor will be called with 2 Complex::Complex(2.0, 0.0) and then the copy constructor will be called to create a with a copy of Complex(2.0. 0). Isn't it correct ?

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


When you write

Complex a = 2;

the compiler will not directly call the Complex constructor using 0 as default argument to build a but instead it will consider if it can "convert" 2 to a Complex.

To do the conversion it will find your Complex(re,im) version and could use that thanks to the default value and to the fact you didn't declare your constructor explicit, but then it will have to find a way transfer this value to a.

The tool for this "transfer" could be a copy constructor. However the complex value that can be built with Complex(re,im) is a temporary, and for some questionable reasons in C++ you are not allowed to pass a temporary as a non-const reference to a function.

So your copy constructor cannot be used with the temporary and the compiler is stuck as there are no ways to initialize a using 2.

If you declare your copy constructor instead accepting a const reference then the temporary can be passed to your copy constructor to initialize a and so everything works as you expect.

Directly initializing a could have been done using the syntax Complex a(2), that in this case doesn't need to use the copy constructor.

Note also that as strange it may be when you use the syntax Complex a = ... the compiler must check if it's legal to use a copy constructor, but once that legality has been checked it is allowed to not call it and use a direct initialization instead. In other words even if you need to declare your copy constructor accepting a const reference to be able to compile still the compiler may actually skip that part and directly build a without calling the copy constructor (even if the copy constructor - as in your case - has side effects). This apparently crazy rule has been added to be able to allow some optimizations in the generated code.


Copy constructors in C++ require the const part of the argument in order to take a const parameter, as you discovered. Otherwise you didn't create a copy constructor that can take a const argument, you created a copy constructor that takes a non-const Complex& as an argument.


You always create a copy construct with & so you don't have to copy the whole object for the function to use it. Creating an object copy takes time, referencing it is much more efficient.

In any case, it is required to preceed your object parameter with const so that the copy constructor is guaranteed not to change the input object. For instance:

Complex::Complex(const Complex &c)
{
        cout << "Copy constructor called " << endl;
        re = c.re;
        im = c.im;
        c.im = 'something'; // This would not work
}

Regards,
Dennis M.


The problem with this code is that your copy constructor is defined with this signature:

Complex::Complex(Complex &c)

This takes a non-const reference as a parameter, which probably isn't what you want. This would mean, for example, that if you try to copy a Complex object with the copy constructor, you'd be allowed to modify the original object!

To fix this, change your code to take the Complex by const reference:

Complex::Complex(const Complex &c)

More generally, copy constructors and assignment operators should always take in their arguments by const reference unless you have a very strong reason to think otherwise.

There are a few other things in your code I should probably point out. For starters, in this case, your copy constructor isn't necessary because it just does a straight copy of all the fields. There's a rule of thumb called the "rule of three" that says that you should only have a copy constructor if you have a destructor (and then you should also have an assignment operator). Otherwise, the default functions provided by the compiler should probably be sufficient for what you're doing.

Also, there's no reason to write your own Complex class, unless you absolutely must. The <complex> header defines complex<T> as a library class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜