Россия и Беларусь начали и продолжают войну против целого народа Украины!

Урок №142. Копирующая инициализация

  Юрий  | 

  |

  Обновл. 13 Сен 2021  | 

 33459

 ǀ   9 

Рассмотрим следующую строку кода:

Здесь используется копирующая инициализация для инициализации целочисленной переменной a значением 7. С обычными переменными всё просто. Однако с классами дела обстоят несколько сложнее, поскольку в их инициализации используются конструкторы. На этом уроке мы рассмотрим использование копирующей инициализации с классами.

Использование копирующей инициализации с классами

Рассмотрим следующую программу:

Результат выполнения программы:

7/1

Форма копирующей инициализации в языке C++ в вышеприведенном примере обрабатывается точно так же, как и следующая:

А, как мы уже знаем из предыдущего урока, это может привести к вызову как Drob(int, int), так и конструктора копирования Drob (который может быть проигнорирован). Однако, поскольку гарантии на 100% игнорирования конструктора копирования не предоставляется, то лучше избегать использования копирующей инициализации при работе с классами и вместо нее использовать прямую инициализацию или uniform-инициализацию, так как в случае с использованием конструктора копирования у вас может получиться следующий результат:

7

Вместо необходимого:

7/1

Так как в конструкторе копирования (который язык C++ предоставит автоматически) значения по умолчанию для m_denominator не будет.

Правило: Избегайте использования копирующей инициализации при работе с классами, вместо нее используйте uniform-инициализацию.

Другие применения копирующей инициализации


Когда вы передаете или возвращаете объект класса по значению, то в этом процессе используется копирующая инициализация. Рассмотрим следующую программу:

Здесь функция makeNegative() принимает объект класса Drob по значению и возвращает его так же по значению. Результат выполнения программы:

Copy constructor worked here!
Copy constructor worked here!
-6/7

Первый вызов конструктора копирования выполнится при передаче sixSeven в качестве аргумента в параметр d функции makeNegative(). Второй вызов выполнится при возврате объекта из функции makeNegative() обратно в функцию main(). Таким образом, объект sixSeven копируется дважды.

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

В этом случае компилятор, скорее всего, проигнорирует использование конструктора копирования, хоть объект x и возвращается по значению.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (165 оценок, среднее: 4,71 из 5)
Загрузка...

Комментариев: 9

  1. Павел:

    Не совсем ясно, почему "в конструкторе копирования (который язык C++ предоставит автоматически) значения по умолчанию для m_denominator не будет." если будет создан анонимный объект, для чего будет вызван конструктор по умолчанию с проверкой на 0. Даже в предыдущем уроке в явно предоставленном конструкторе копирования есть комментарий "// Нет необходимости выполнять проверку denominator здесь, так как она осуществляется в конструкторе по умолчанию".

    1. Ninedenon:

      Дело в почленной инициализации, объект копия копирует члены объекта оригинала

  2. 27th:

    В разделе: "Другие применения копирующей инициализации"
    в коде на 37 строке есть комментарий: "// правильно было бы здесь использовать константную ссылку". Вопрос: Почему?
    Константная ссылка не позволит нам использовать функцию-член setNumerator().

  3. Валерий:

    Поддержу предыдущего оратора. И добавлю.
    Результат 7 получиться никак не может.
    Кто откусил вывод косой и знаменателя. Оператор << никак не меняется.
    Скорее всего речь идет о выводе 7/0.

    1. Андрей:

      Да что-то и 7/0, наверное, никогда не получится. В случае конструктора копирования будет создан анонимный объект, конструктор для него знаменатель укажет 1 (по умолчанию), а потом конструктор копирования (компилятора) сделает присвоение из анонимного объекта каждому члену (числителю и знаменателю) для вновь создаваемого объекта .

      1. Grave18:

        В Debug версии вызывается конструктор по умолчанию один раз, а в Release версии даже конструктор вызываться не будет, все сразу на консоль выведется, судя Disassembly в VS2019. Сложно вот так на вид судить, что там компилятор вызывает, пока сам не посмотришь.

  4. WJ:

    Немного не понятно, как следующий код:

    может выдать результат 7 в случае если не будет оптимизации?

    При вызове Drob(int, int) получим m_numerator = 7 и m_denominator = 1 в анонимном объекте, которые почленно скопируются дефолтным конструктором копирования в seven

    1. Surprizze:

      Наверное этот парень уже прошел все уроки, но отвечу все равно.

      Речь шла не об:

      а об:

      в последнем случае результат может быть 7, так как конструктор который мы указали не вызовется(вызовется копирующий конструктор, в котором мы не указали параметры по умолчанию).

      1. Максим:

        Копирующий конструктор, который не явно будет предоставлен классу, выполняет почленное копирование. Значит Drob seven должна быть 7/1. Разве не так?

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *