C ++ конструкторы по умолчанию - PullRequest
3 голосов
/ 01 февраля 2011

Я получаю ошибку компиляции со следующим кодом:

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 ]

9 голосов
/ 01 февраля 2011

Когда вы пишете

Complex a = 2;

, компилятор не будет напрямую вызывать конструктор Complex, используя 0 в качестве аргумента по умолчанию для построения a, но вместо этого он будет учитывать, сможет ли он "преобразовать" 2 вComplex.

Чтобы выполнить преобразование, он найдет вашу Complex(re,im) версию и сможет использовать ее благодаря значению по умолчанию и тому факту, что вы не объявили свой конструктор explicit, но тогда он будетнужно найти способ передачи этого значения в a.

Инструментом для этого «переноса» может быть конструктор копирования.Однако комплексное значение, которое может быть построено с помощью Complex(re,im), является временным , и по некоторым сомнительным причинам в C ++ вам не разрешено передавать временное значение как неконстантную ссылку на функцию.

Таким образом, ваш конструктор копирования не может использоваться с временным, и компилятор застрял, так как нет способов инициализировать a, используя 2.

Если вы объявляете ваш конструктор копирования вместо принятия ссылки constзатем временная переменная может быть передана в ваш конструктор копирования для инициализации a, и все будет работать так, как вы ожидаете.

Непосредственная инициализация a могла бы быть выполнена с использованием синтаксиса Complex a(2), который в данном случае нене нужно использовать конструктор копирования.

Обратите внимание, что, как ни странно, может быть, когда вы используете синтаксис Complex a = ..., компилятор должен проверить, допустимо ли использовать конструктор копирования, но как только эта законность была достигнутапроверил, что разрешено не вызывать его и использовать вместо него прямую инициализацию.Другими словами, даже если вам нужно объявить, что ваш конструктор копирования принимает ссылку на const для возможности компиляции, компилятор может фактически пропустить эту часть и напрямую собрать a без вызова конструктора копирования (даже если конструктор копирования - как в вашемслучай - имеет побочные эффекты).Это явно сумасшедшее правило было добавлено, чтобы позволить некоторую оптимизацию в сгенерированном коде.

1 голос
/ 01 февраля 2011

Вы всегда создаете конструкцию копирования с &, поэтому вам не нужно копировать весь объект, чтобы функция использовала его. Создание копии объекта занимает много времени, ссылаться на него гораздо эффективнее.

В любом случае перед параметром объекта необходимо указать const, чтобы конструктору копирования гарантировалось , а не для изменения объекта ввода. Например:

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

С уважением,
Деннис М.

1 голос
/ 01 февраля 2011

Проблема с этим кодом заключается в том, что ваш конструктор копирования определен с такой сигнатурой:

Complex::Complex(Complex &c)

В качестве параметра используется ссылка не const, что, вероятно, не то, что вам нужно.Это будет означать, например, что если вы попытаетесь скопировать объект Complex с помощью конструктора копирования, вам будет разрешено изменить исходный объект!

Чтобы исправить это, измените код так, чтобы он принималComplex по const ссылка:

Complex::Complex(const Complex &c)

В более общем случае конструкторы копирования и операторы присваивания всегда должны принимать свои аргументы посредством константной ссылки, если у вас нет веских оснований думать иначе.

В вашем коде есть еще несколько вещей, на которые я, вероятно, должен обратить внимание.Для начала, в этом случае, ваш конструктор копирования не нужен, потому что он просто делает прямую копию всех полей.Существует практическое правило, называемое «правилом трех», которое гласит, что у вас должен быть только конструктор копирования, если у вас есть деструктор (а затем у вас также должен быть оператор присваивания).В противном случае функции по умолчанию, предоставляемые компилятором, вероятно, будут достаточны для того, что вы делаете.

Кроме того, нет причин писать свой собственный класс Complex, за исключением случаев, когда это абсолютно необходимо.Заголовок <complex> определяет complex<T> как класс библиотеки.

1 голос
/ 01 февраля 2011

Конструкторам копирования в C ++ требуется часть аргумента const, чтобы получить параметр const, как вы обнаружили. В противном случае вы не создали конструктор копирования, который может принимать аргумент const, вы создали конструктор копирования, который принимает в качестве аргумента неконстантный Complex&.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...