Конструктор перезагрузки с параметрами

Обновлено: 25.04.2024

Столкнулся с проблемой: При такой записи, как ниже, все работает и все прекрасно компилируется:

Но если я добавляю конструктор копирования, то у меня вылетает ошибка:

Подскажите: Как правильно перегрузить конструкторы так, чтобы не было этого конфликта?

Рассмотрим проблему подробнее.

Стандарт обязывает понимать

как user-defined конструктор копирования. Поэтому, конструктор копирования T::T(const T&) не будет объявлен по-умолчанию компилятором. Со стандарта C++11 предоставляется возможность заставить компилятор генерировать неявно-объявленный конструктор копирования ключевым словом default

Неявно объявленный конструктор копирования по-умолчанию имеет сигнатуру T::T(const T&) но лишь в случае, когда все базовые классы T имеют конструкторы копирования с параметрами const B& или const volatile B&, и когда все не статические данные-члены класса T имеют конструкторы копирования с параметрами const M& или const volatile M&.

В ином случае, неявно объявленный конструктор копирования имеет сигнатуру

В данном случае, конструктор копирования A(A& obj) является тривиальным с сигнатурой сгенерированного неявно-определённого конструктора копирования T::T(T&).
Тривиальное копирование практически аналогично std::memmove

В строке ошибки компиляции происходит следующее:

1) оператор = в строке с ошибкой компиляции не является инициализирующим, поскольку литерал 5 является (rvalue, нельзя взять адрес) const int. Для исполнения операции присваивания компилятор сначала конструирует A(5);

2) операция присваивания имеет дело с A obj = A (5):

Учитывая вышесказанное, для исправления ошибки компиляции можно либо удалить строку
A(A& obj); ,
что приведёт к неявному определению компилятором тривиальных конструкторов копирования и перемещения, либо добавить ещё в объявление класса A строку
A(A&& obj);

С точки зрения стандарта С++11 и выше, можно утверждать, что выражения A(A& obj); и A(A&& obj); соответственно эквивалентны A(A& obj) = default; и A(A&& obj) = default;

В статье Конструктор и деструктор класса мы уже встречались с перегруженным конструктором в коде, но не акцентировали на этом внимание. Перегрузка конструкторов очень схожа с перегрузкой функций. Конструкторов в определяемом классе может быть несколько – по мере необходимости. Они должны иметь одинаковое имя, идентичное имени класса и обязательно должны отличаться сигнатурой.

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

перегруженный конструктор класса с++, основы программирования для начинающих, c++ с нуля

На этом примере достаточно легко увидеть, чем вызвана необходимость перегрузки конструкторов. Основной смысл в том, чтобы дать возможность программисту выбрать наиболее подходящий способ инициализации объекта.

Тут представлен самый распространенный вариант перегрузки конструкторов. А именно конструктор с параметрами и второй без параметров. Часто, программисту бывают необходимы оба подобных конструктора, так как конструктор с параметрами удобно использовать, работая с одиночными объектами. Но он не может быть использован, например, для инициализации динамического массива объектов класса.

При каждом объявлении объекта класса надо применять соответствующий определённым конструкторам способ объявления.

Конструктор можно перегружать столько раз, сколько посчитаете нужным. Но, как бывает у большинстве случаев, желательно придерживаться золотой середины. Перегружайте его только для наиболее распространенных ситуаций.

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

Возможно вы заметили, что определяя класс, мы не можем инициализировать его поля (члены) в самом определении. Можно присвоить им значение, написав соответствующий метод класса и вызвав его, после создания объекта вне класса. Такой способ не совсем удобен, так как объявляя, допустим, 33 объекта класса нам придется 33 раза вызывать метод, который присваивает значения полям класса. Поэтому, как правило, для инициализации полей класса, а так же для выделения динамической памяти, используется конструктор.

Конструктор (от construct – создавать) – это особый метод класса, который выполняется автоматически в момент создания объекта класса. То есть, если мы пропишем в нем, какими значениями надо инициализировать поля во время объявления объекта класса, он сработает без “особого приглашения”. Его не надо специально вызывать, как обычный метод класса.

В строках 11 – 17 определяем конструктор: имя должно быть идентично имени класса; конструктор НЕ имеет типа возвращаемого значения (void в том числе). Один объект объявляется сразу во время определения класса – строка 25. При запуске программы, конструктор этого объекта сработает даже до входа в главную функцию. Это видно на следующем снимке:

конструктор класса c++, деструктор класса c++, конструктор и деструктор класса с++

программа еще не дошла до выполнения строки 29 setlocale ( LC_ALL , "rus" ) ; , а конструктор уже “отчитался”, что сработал (кириллица отобразилась некорректно). В строке 30 – смотрим, что содержат поля класса. Второй раз конструктор сработает в строке 32, во время создания объекта obj2 .

Деструктор (от destruct – разрушать) – так же особый метод класса, который срабатывает во время уничтожения объектов класса. Чаще всего его роль заключается в том, чтобы освободить динамическую память, которую выделял конструктор для объекта. Имя его, как и у конструктора, должно соответствовать имени класса. Только перед именем надо добавить символ ~

Добавим деструктор в предыдущий код. И создадим в классе два конструктора: один будет принимать параметры, второй – нет.

Конструкторы могут иметь параметры. Для этого просто нужно добавить эти параметры в объявление и определение конструктора, а затем, при создании объекта, задать их в качестве аргументов. Теперь к нашим знаниям добавим еще одно - конструкторов может быть несколько.

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

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

Пример.

Комментарии к примеру и особенности использования.

Каждому способу объявления объекта класса должна соответствовать своя версия конструкторов класса. Если это не будет обеспечено, то при компиляции программы обнаружится ошибка на этапе компиляции.

На этом примере легко понять, чем может быть вызвана необходимость перегрузки конструкторов.(именно перегрузки, поскольку речь здесь идет о функциях, имеющих одинаковые имена, но различные списки параметров) Итак, главный смысл перегрузки конструкторов состоит в том, чтобы предоставить программисту наиболее подходящий метод инициализации объекта.

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

Хотя конструктор можно перегружать столько раз, сколько захотите, лучше не стоит этим злоупотреблять. Конструктор стоит перегружать лишь для наиболее часто встречающихся ситуаций.

Обратите внимание на то, что тела конструкторов описаны за пределами класса. В класс помещены только прототипы. Данная форма записи может быть использована и для обычных методов класса. Напомним, что в примерах прошлого урока тела методов описывались прямо в определении класса.

Cпособ, используемый в описанном выше примере, предпочтительнее для сложных методов. Объявленные таким образом функции автоматически заменяются компилятором на вызовы подпрограмм.


Мы можем перегрузить конструкторы несколькими способами:

  • Используя аргументы другого типа
  • Используя разное количество аргументов
  • Используя другой порядок аргументов

Разные типы данных параметров

Пример:

Здесь имя класса ADD . В первом конструкторе есть два параметра, первый - int, другой - float, а во втором конструкторе также есть два параметра, первый - строкового типа, а другой - типа int .
Здесь конструкторы имеют одно и то же имя, но типы параметров разные, что похоже на концепцию перегрузки метода.

Изменение количества параметров

В этом случае мы будем использовать два или более конструктора с разным количеством параметров. Типы данных аргументов могут быть одинаковыми, но количество параметров будет другим.

Пример:

Здесь имя класса - ADD . В первом конструкторе количество параметров равно двум, а типы параметров - int . Во втором конструкторе количество параметров равно трем, и типы параметров также являются int

Конструкторы с разным порядком объявления параметров

Здесь два конструктора содержат одни и те же типы параметров, то есть каждый конструктор имеет один тип double , один тип int и один параметр string типа, но позиции параметров различаются. Компилятор вызовет конструктор в соответствии с порядком их аргументов.

Вызов перегруженного конструктора с помощью ключевого слова this

Мы можем вызвать перегруженный конструктор из другого конструктора , используя это ключевое слово , но конструктор должен быть принадлежать к тому же классу, потому что это ключевое слово указывает члены одного и того же класса , в котором это используется. Этот тип вызова перегруженного конструктора также называется цепочкой конструкторов .

Пример:

Здесь первый конструктор является конструктором по умолчанию, второй и третий конструкторы - это параметризованный конструктор, один из которых имеет тип int, а другой - параметр типа double .

Во втором конструкторе this() вызывает первый конструктор, который является конструктором по умолчанию. Здесь после этого ключевого слова есть only() , что означает, что компилятор вызывает конструктор без аргументов, означает конструктор по умолчанию.

В третьем конструкторе this(int) вызывает второй конструктор, который является параметризованным конструктором. Здесь после этого стоит (int) , что означает, что компилятор вызывает конструктор с аргументом типа int.

Перегрузка конструктора копирования

Параметризованный конструктор, содержащий параметр того же типа класса, называется конструктором копирования. По сути, конструктор копирования - это конструктор, который копирует данные одного объекта в другой объект. Его основное использование - инициализировать новый экземпляр значениями существующего экземпляра.

Читайте также: