Перегрузка унарных операций на примере инкремента ++

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


Из-за вложенной идеологии в эти две формы перегрузкой операций инкремента (а также и декремента) предполагается негласное учитывание этой особенности. Т. е. если перегружаем ++, то правильно делать это по тем же принципам, которые заложены во встроенную операцию ++, применяемую ко встроенным типам данных.
  • При перегрузке постфиксной формы нужно использовать фиктивный параметр. Достаточно обозначить тип int.
  • При перегрузке префиксной формы список параметров пуст, но нужно учитывать особенность того, что объект сначала обновляет своё состояние и только потом используется.
Начнём с перегрузки префиксной формы ++.

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

Перегрузка постфиксной формы делается немного более сложно. Используется так называемый фиктивный параметр, который нужен только в качестве подсказки компилятору, что форма операции постфиксная. Такой параметр достаточно обозначить типом int, в коде ниже вы увидите, что это просто. Условная сложность заключается только в том, что из-за необходимости учитывания особенности использования значения с дальнейшим его изменением, сначала создаёт временный объект, в который копируется исходное состояние изменяемого объекта, потом оригинальный объект изменяют, а возвращают не ссылку на оригинальный объект, а тот временный объект, в котором сохранено ещё неизменённое состояние уже изменённого объекта. На словах сложновато, но посмотрев код вы всё обязательно поймёте.

Есть два отличия, которые могут броситься в глаза новичку: в одном случае возврат происходит по ссылке, в другом не по ссылке и отличие с фиктивным параметром и без. О фиктивном параметре было уже пояснено: он нужен как подсказка компилятору о форме операции. А вот возврат по ссылке или не по ссылке имет значение.
Когда мы не создаём внутри метода-перегрузки локального по отношению к этому методу объекта, то возвращаем по ссылке. Если создавать локальный объект внутри функции, то с прекращением работы функции локальный объект будет уничтожен, и если возвращать ссылку на такой объект, то получится, что ссылка будет привязана к уничтоженному объекту, а это нехорошо. Поэтому в случае с перегрузкой постфиксной формы возвращают не ссылку. В случае с префиксной перегрузкой временного объекта не создают и возвращают объекту чуть ли не самого себя, только обновлённого. Чтобы не создавать лишних копий (ведь создание любых временных объектов это дополнительные затраты, часто бьющие по эффективности) объекту возвращают ссылку на самого себя. Только по этим причинам в одном случае используют для возврата ссылку, а в другом не используют.
Обычно перегрузку инкремента (а также декремента) выполняют по этому принципу.
Также можно делать пергузку ++ в качестве friend. Чтобы сделать такую, нужно понимать, что friend-функции не являются частью класса. Поскольку они не часть класса, то не знают об объектах, следовательно таким нужно подсказывать параметром про используемый объект. В остальном всё делается также: для префиксной формы фиктивный параметр не нужен, а для постфиксной нужен и параметр и учитывание постфиксной особенности:

Нельзя совместить дружественную и недружественную вариацию. Если вам достаточно недружественной, то используйте недружественную. Дружественный варант показан только на всякий случай, если кому-то действительно оно нужно, но по каким-то причинам не хватает некоторых сведений.
В любом случае основной принцип один. Поскольку можно запутаться, где создавать временный объект, а где не надо, можно запомнить, что где нужен фиктивный параметр, там нужен временный объект. А чтобы не запутаться с ссылкой просто помнить, что ссылка на временный объект потенциальная угроза.
Ещё немного слов о фиктивном параметре. Обычно его не используют, но использовать его возможно, например, вот таким образом:

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

Хотя для нас создано так, чтобы мы этого не замечали и использовали сокращённую форму:

Если вы до сих пор не понимаете арност (унарный, бинарный), то, возможно, последнее вам поможет понять как операции зависят от количества аргументов. Несмотря на то, что листинг #4 показан и полностью рабочий, более простой вариант реализации подобного поведения выполняется с помощью перегрузки операции +=. Листинг #4 показан только для того, чтобы вы не начали думать, что фиктивный параметр вообще бесполезен. Использовать параметр, если прям невероятно сильно нужно, можно в таком явном виде.

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

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

Поиск

 
     

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

https://www.litres.ru/maykl-neygard/release-it-proektirovanie-i-dizayn-po-dlya-teh-komu-ne-vse-ravno-16901930/?lfrom=15589587
Яндекс.Метрика