Урок 141. Конструктор копирования

   ⁄ 

 Обновлено 6 мая 2018  ⁄ 

 ⁄   2 

⁄   275

Вспомним все типы инициализации, которые поддерживает C++: прямая инициализация, uniform инициализация и копирующая инициализация.

Рассмотрим примеры всех инициализаций выше на практике, используя следующий класс Drob:

Мы можем выполнить прямую инициализацию:

В C++11 мы можем выполнить uniform инициализацию:

И, наконец, мы можем выполнить копирующую инициализацию:

С прямой и uniform инициализациями создаваемый объект непосредственно инициализируется. Однако с копирующей инициализацией дела обстоят несколько сложнее. Мы рассмотрим это детальнее в следующем уроке. Но перед этим нам еще нужно в кое-чем разобраться.

Конструктор копирования

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

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

6/7

Рассмотрим подробнее, как работает эта программа.

С объектом sixSeven выполняется обычная прямая инициализация, которая приводит к вызову конструктора Drob(int, int). Здесь нет никаких сюрпризов. А вот инициализация объекта dCopy также является прямой инициализацией, но какой конструктор вызывается здесь? Ответ: конструктор копирования.



Конструктор копирования — это особый тип конструктора, который используется для создания нового объекта через копирование существующего объекта. И, как в случае с конструктором по умолчанию, если вы не предоставите конструктор копирования для своих классов самостоятельно, то C++ создаст public конструктор копирования сам. Поскольку компилятор мало знает о вашем классе, то по умолчанию, созданный конструктор копирования будет использовать почленную инициализацию. Почленная инициализация означает, что каждый член объекта-копии инициализируется непосредственно из члена объекта-оригинала. Т.е. в примере выше dCopy.m_numerator будет иметь значение sixSeven.m_numerator (6), а dCopy.m_denominator будет равен sixSeven.m_ denominator (7).

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

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

Copy constructor worked here!
6/7

Конструктор копирования в примере выше использует почленную инициализацию и функционально эквивалентен конструктору по умолчанию, за исключением того, что мы добавили стейтмент вывода, в котором указали текст (сработал конструктор копирования).

Предотвращение создания копий объектов

Мы можем предотвратить создание копий объектов наших классов, сделав конструктор копирования закрытым:

Здесь мы получим ошибку компиляции, так как dCopy должен использовать конструктор копирования, но не видит его, поскольку конструктор копирования является закрытым.

Конструктор копирования может быть проигнорирован

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

Сначала инициализируется анонимный объект Drob, который приводит к вызову конструктора Drob(int, int). Затем этот анонимный объект используется для инициализации объекта sixSeven класса Drob. Поскольку анонимный объект является объектом класса Drob, как и sixSeven, то здесь должен вызываться конструктор копирования, верно?

Запустите эту программу самостоятельно. Ожидаемый результат:

Copy constructor worked here!
6/7

Реальный результат:

6/7

Почему наш конструктор копирования не сработал?

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

По этой причине в таких случаях компилятору разрешается отказаться от вызова конструктора копирования и просто выполнить прямую инициализацию. Этот процесс называется элизия (elision).



Поэтому, даже если вы напишите:

Компилятор может изменить это на:

В последнем случае с прямой инициализацией потребуется вызов только одного конструктора (Drob(int, int)). Обратите внимание, в случаях, когда используется элизия, любые стейтменты в теле конструктора копирования не выполняются, даже если они имеют побочные эффекты (например, выводят что-либо на экран)!

Наконец, если вы делаете свой конструктор копирования закрытым, то любая инициализация, использующая этот закрытый конструктор копирования, приведет к ошибкам компиляции, даже если конструктор копирования будет проигнорирован!

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

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (5 оценок, среднее: 5,00 из 5)
Загрузка...
Подписаться на обновления:

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

  1. Crafty Fox:

    Спасибо тебе большое за этот титанический труд по переводу! Уроки действительно очень хорошие и легко читаются. Надеюсь, у тебя хватит сил и терпению перевести их до конца. Думаю, они многим помогут выучить С++.

    1. Юрий:

      Спасибо за отзыв. Надеюсь, что сил и терпения действительно хватит 🙂

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

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

ПОДПИСЫВАЙТЕСЬ

НА КАНАЛ RAVESLI В TELEGRAM

@ravesli

ПОДПИСАТЬСЯ БЕСПЛАТНО