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

  Юрий  | 

  |

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

 84547

 ǀ   10 

Оператор присваивания (=) используется для копирования значений из одного объекта в другой (уже существующий) объект.

Присваивание vs. Конструктор копирования

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

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

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

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


Перегрузка оператора присваивания (=) довольно-таки проста и выполняется через метод класса, но есть один нюанс:

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

6/7

До этого момента всё ок. Функция перегрузки operator=() возвращает скрытый указатель *this, и мы даже можем связать выполнение нескольких операций присваивания вместе:

Самоприсваивание

Здесь уже становится интереснее. Самоприсваивание — это тот нюанс, о котором упоминалось выше. Язык C++ позволяет выполнять самоприсваивание:

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

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

Запустите программу, и вы увидите, что выведется Anton, как и ожидалось.

Теперь замените функцию main() на следующую:

В результате вы получите либо значение-мусор, либо сбой.

Рассмотрим, что происходит при выполнении операции присваивания, когда неявный и переданный в качестве аргумента объекты являются объектом anton. В этом случае m_data равно str.m_data (т.е. Anton). Первое, что произойдет — функция перегрузки проверит, является ли уже значением неявного объекта строка Anton. Если является, то произойдет удаление этого значения, чтобы не случилась утечка памяти. Т.е. значение m_data неявного объекта удаляется, но дело в том, что str.m_data имеет тот же адрес памяти (значение которого удаляется)! Это означает, что str.m_data станет висячим указателем.

Позже, когда мы будем копировать данные из параметра str функции перегрузки в наш неявный объект, мы будем обращаться к висячему указателю str.m_data. Это приведет к тому, что мы либо скопируем данные-мусор, либо попытаемся получить доступ к памяти, которую наше приложение больше не имеет в распоряжении (произойдет сбой).

Обнаружение и обработка самоприсваивания


К счастью, мы можем обнаружить выполнение самоприсваивания. Это делается с помощью достаточно простой проверки в функции перегрузки operator=():

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

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

Оператор присваивания по умолчанию

В отличие от других операторов, компилятор автоматически предоставит открытый оператор присваивания по умолчанию для вашего класса при его использовании, если вы не предоставите его самостоятельно. В операторе присваивания по умолчанию выполняется почленное присваивание (которое является аналогичным почленной инициализации, используемой в конструкторах копирования, предоставляемых языком C++ по умолчанию).

Как и с другими конструкторами и операторами, вы можете запретить выполнение операции присваивания с объектами ваших классов, сделав оператор присваивания закрытым или используя ключевое слово delete:


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

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

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

  1. Сергей:

    Добрый день! Можно пояснить для чего необходимо это действие.

    1. Антон:

      Если m_data существует и в нем что-то хранится, то данная область памяти очищается, т.к. в будущем произойдет новое выделение памяти под m_data с размером, как у SomeString &str, так как мы совершаем операцию присваивания

  2. Денис:

    Доброго времени суток!
    Столкнулся с такой проблемой.
    Есть объект, в котором переопределен оператор присваивания, а также деструктор, который освобождает динамически выделенную память. Соответственно при помощи оператора присваивания мы просто перевешиваем указатели на новый объект (который собственно присваивает себе область памяти старого объекта). Как в таком случае реализовать деструктор, чтобы одна и та же память не освобождалась многократно?

  3. Виталий:

    Весьма хорошая подача материала, Вы большой молодец!

  4. Артурка:

    Оператор присваивания можно реализовать чуть проще:
    Реализовываем дружественную/обычную ф-цию swap:

    Далее реализовать оператор присваивания через swap:

    В этом случае используется не традиционная реализация оператора:
    1) аргумент принимается по значению
    2) Данные текущего класса меняются с местами другого класса
    3) Возвращается ссылка на текущий класс
    4) Временный объект other удаляется по выходу из тела функции

  5. Андрей:

    А есть где то описание того что если определить какой то из Конструкторов по умолчанию, копирование, и копирующего присваивания — то все остальные не генерируются если не попросить этого явно? толи я пропустил тему где этот момент описан..

    1. Алексей:

      Если вы определите собственный конструктор то автоматически генерируемый конструктор генерироваться не будет. Но только он. Это же касается отдельно оператора присваивания и конструктора копирования

  6. Герман:

    Спасибо за огромный труд, с нетерпением жду следующих уроков!!!!!

    1. Фото аватара Юрий:

      Спасибо, что читаете 🙂

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

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