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

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

  Юрий  | 

  |

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

 33971

 ǀ   10 

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

Здесь используется копирующая инициализация для инициализации целочисленной переменной 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 (169 оценок, среднее: 4,72 из 5)
Загрузка...

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

  1. Павел:

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

    1. Ninedenon:

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

  2. 27th:

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

    1. Павел:

      Сеттер мы не можем сделать константным, поэтому объект в функцию принимаем просто по ссылке.
      На уроке №98 мы рассмотрели преимущества передачи аргументов по константной ссылке, нежели по значению. Если вкратце, то передача аргументов по значению создает копию значения (что является медленным процессом). Большую часть времени нам не нужна копия, а ссылка уже указывает на исходный аргумент и является более эффективной, так как избегает создания и использования ненужной копии. Мы обычно делаем ссылку константной для гарантии того, что функция не изменит значение аргумента и сможет работать с r-values (например, с литералами).

      А возврат можно сделать уже и константным, если нужен константный объект.

      Константный объект может вызывать только константные методы. Константный метод не может вызывать неконстантные методы.

      https://ravesli.com/urok-123-klassy-i-const/#toc-1

  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 не будет опубликован. Обязательные поля помечены *