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

Операция присваивания нам, наверное, всем хорошо известна. Обознается она как =. Эта операция требует себе двух участников: объект, в который присваивается значение, и объект, который выполняет роль присваиваемого. Следовательно, операция бинарная. Это значит, что пишется реализация перегрузки операции = по тому же принципу, какой у +, +=, * и любой другой бинарной операции.
Поскольку операция присваивания предполагает собой копирование состояний, то в реализации перегруженной функции всё сводится к банальному копированию значений. Возвращается из функции-перегрузки ссылка объекта на самого себя, т. е. *this.
Есть различные варианты реализаций операций присваивания. Поскольку мы в большинстве случаев присваиваем объекты одного и того же типа, а операция присваивания умеет генерироваться неявно, случаи без ручного управления памятью не так интересны и не дают некоторых необходимых сведений.
Когда происходит ручное правление памятью (например, используется операция new), присваивание (оно же — копирование) осложняется. Одна из сложностей (которая легко обходится простой проверкой) — присваивание объекта самому себе.

  • Поскольку объект-приёмник может ссылаться на данные, для которых уже была распределена память, функция должна использовать операцию delete [ ] для её освобождения.
  • Функция-перегрузки операции присваивания должна содержать защиту от присваивания объекта самому себе — иначе освобождение памяти может стереть содержимое объекта до того, как оно будет переустановлено.
  • Функция-перегрузки операции присваивания возвращает ссылку на вызывающий объект.
Эти знания необходимы для того, чтобы использованную в статье основу, взятую в качестве примера, вы понимали.


В заложенной основе нет перегруженной операции присваивания. Несмотря на это сама операция присваивания работает, но вот то как она работает, это проблема:


Операция присваивания умеет генерироваться неявно. И если мы явно её не обозначаем, она неявно генерируется компилятором. Вариант, сгенерированный компилятором, выполняет поверхностное копирование. Поскольку в классе у нас указательная переменная, которой выделяется память, то при поверхностном копировании копируется адрес указателя, но не область памяти, на которую указательная переменная указывает. В этом заключается основная проблема. Чтобы объекты друг на друга никак не влияли, нам нужно создать дополнительное пространство в памяти, в которое скопировать ту область памяти, на которую указательная переменная, адрес которой был скопирован в неудачном коде, указывает. Чтобы это исправить, нужно описать функцию-перегрузки операции присваивания явно, своими силами. Реализация функции-перегрузки операции присваивания подобна реализации конструктора копирования. На самом деле есть различные способы реализаций, но первым будет показан способ, которому обычно учат новичков.
Операция присваивания относится к бинарным операциям, поскольку при её активности используется два участника: объект, в который что-то присваивается, и присваиваемый объект, состояние которого копируется. Следовательно, перегрузка сильно похожа на перегрузку любых бинарных операций, как, например, +, += и др. Но как упоминалось ранее, важно проверять факт присваивания самого в себя: иначе можно почистить память до того как состояние памяти успеет скопироваться. Возвращают ссылку, чтобы не происходило лишних копирований.

В случае со строкой не нужно дополнительно учитывать длину, потому что благодаря нуль-символу всегда можно узнать актуальную длину Си-строки. Но в случае написания класса, где не будет возможности подсчёта необходимых количеств, эти количества нужно будет копировать в функции-перегрузке операции присваивания дополнительно. Чтобы не получить огрызок, нужно копировать все важные составляющие объекта: будь это количество ячеек, вместимость или что-то ещё.
Особо ничего сложного в перегрузке операции присваивания нет, только помнить нужно о возможности присваивания самого в себя и избегать это простым условием, что продемонстрировано в листинге #2.
Основу вы уже узнали, сейчас речь пойдёт о других вариантах реализации. В С++11 появился спецификатор noexcept. Этот спецификатор указывает, может ли функция вызывать исключения. Поскольку я не знаком с исключениями, то именно предполагаемый сейчас пример подробным образом объяснить не сумею. Просто оставлю на этой странице сам пример и напишу как оно называется. Есть в С++ идиома, называется copy-and-swap. Копия-и-обмен. Чтобы использовать эту идиому в коде, нужно будет дописать конструктор копирования. Внутри функции-перегрузки операции присваивания будет создан временный объект, в который благодаря копирующему конструктору будет сохранено состояние присваиваемого объекта. Кроме этого, нужно будет описать реализацию обмена. Использование идиомы даёт больше удобства относительно описанного в этой статье способа, но за удобство нужно будет платить бо́льшими ресурсозатратами.
Модифицируем листинг #2 путём использования идиомы copy-and-swap:

Есть ещё один способ реализации, ненадёжный способ. Некоторые люди могут явно вызывать деструктор и тут же создавать копию копируемого объекта в объект-приёмник. В этом варианте также используется копирующий конструктор, но не описывается функция обмена значений. Ненадёжность заключается в том, что если конструктор копирования выкинет исключение, то объект-приёмник может поломаться: получится частичноскопированный объект, из-за чего могущий стать негодным к использованию. В отличие от этого ненадёжного варианта, вариант swap-and-copy предоставляет определённые гарантии. Листинг #4 описывает ненадёжный способ реализации перегрузки операции присваивания. Совет: не использовать такой вариант.

Листинг #4 продемонстрован только для того, чтобы понятно было, что имелось в виду, когда упоминалось о ненадёжном способе реализации перегрузки операции присваивания.
Есть и ещё один способ. Объяснить я его не смогу, ибо он просто списан с других страниц интернета: Перегрузка оператора присваивания C++ и слегка переделан. Но почему бы не показать, да? С++11, смотрите на здоровье, листинг #5:

Все комментарии на сайте проверяются, поэтому ваш комментарий может появиться не сразу. Для вставки кода в комментарий используйте теги: [php]ВАШ_КОД[/php]

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

Ваш e-mail не будет опубликован.

64 − 58 =

Поиск

 
     

Случайная книга в электронном формате

https://www.litres.ru/robert-s-martin/chistyy-kod-sozdanie-analiz-i-refaktoring-2/?lfrom=15589587
Яндекс.Метрика
НАГРАДИ АВТОРА САЙТА
WEBMONEY
R375024497470
U251140483387
Z301246203264
E149319127674

Лучше бы вместо смс ввели

Выражаю свою признательность

  • Максиму очень признателен за указание на мои ошибки и неточности.
  • Sergio ===> за оказание помощи в исправлении моих ошибок
  • Gen ===> за правильное стремление помочь другим новичкам и выявления моих ошибок