Урок №137. Перегрузка операторов инкремента и декремента

  Юрий  | 

  |

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

 46194

 ǀ   12 

Перегрузка операторов инкремента (++) и декремента (−−) довольно-таки проста, но с одним маленьким нюансом. Есть две версии операторов инкремента и декремента: версия префикс (например, ++x, --y) и версия постфикс (например, x++, y--).

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

Перегрузка операторов инкремента и декремента версии префикс

Перегрузка операторов инкремента и декремента версии префикс аналогична перегрузке любых других унарных операторов:

Здесь класс Number содержит число от 0 до 8. Мы перегрузили операторы инкремента/декремента таким образом, чтобы они увеличивали/уменьшали m_number в соответствии с заданным диапазоном (если выполняется инкремент и m_number равно 8, то сбрасываем значение m_number на 0; если выполняется декремент и m_number равно 0, то присваиваем значение 8 переменной m_number).

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

78087

Обратите внимание, мы возвращаем скрытый указатель *this в функциях перегрузки операторов (т.е. текущий объект класса Number). Таким образом мы можем связать выполнение нескольких операторов в одну «цепочку».

Перегрузка операторов инкремента и декремента версии постфикс


Обычно перегрузка функций осуществляется, если они имеют одно и то же имя, но разное количество и типы параметров (детально о перегрузке функций читайте в материалах урока №102). Рассмотрим случай с операторами инкремента/декремента версий префикс/постфикс. Оба имеют одно и то же имя (например, operator++), унарные и принимают один параметр одного и того же типа данных. Как же тогда их различить при перегрузке?

Дело в том, что язык C++ использует фиктивную переменную (или «фиктивный параметр») для операторов версии постфикс. Этот фиктивный целочисленный параметр используется только с одной целью: отличить версию постфикс операторов инкремента/декремента от версии префикс. Выполним перегрузку операторов инкремента/декремента версии префикс и постфикс в одном классе:

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

6778776

Здесь есть несколько интересных моментов:

   Во-первых, мы отделили версию постфикс от версии префикс использованием целочисленного фиктивного параметра в версии постфикс.

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

   В-третьих, операторы версий префикс и постфикс выполняют одно и то же задание: оба увеличивают/уменьшают значение переменной объекта. Разница между ними только в значении, которое они возвращают.

Рассмотрим последний пункт детально. Операторы версии префикс возвращают объект после того, как он был увеличен или уменьшен. В версии постфикс нам нужно возвращать объект до того, как он будет увеличен или уменьшен. И тут конфуз! Если мы увеличиваем или уменьшаем объект, то мы не можем возвратить его до выполнения инкремента/декремента, так как операция увеличения/уменьшения уже произошла. С другой стороны, если мы возвращаем объект до выполнения инкремента/декремента, то сама операция увеличения/уменьшения объекта не выполнится.

Решением является использование временного объекта с текущим значением переменной-члена. Тогда можно будет увеличить/уменьшить исходный объект, а временный объект возвратить обратно в caller. Таким образом, caller получит копию объекта до того, как фактический объект будет увеличен или уменьшен, и сама операция инкремента/декремента выполнится успешно.

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

Наконец, мы реализовали перегрузку операторов версии постфикс через уже перегруженные операторы версии префикс. Таким образом, мы сократили дублированный код и упростили внесение изменений в наш класс в будущем (т.е. упростили поддержку кода).

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

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

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

  1. Антон:

    Рассмотрим последний пункт детально. Операторы версии префикс возвращают объект после того, как он был увеличен или уменьшен. В версии постфикс нам нужно возвращать объект до того, как он будет увеличен или уменьшен. И тут конфуз! Если мы увеличиваем или уменьшаем объект, то мы не можем возвратить его до выполнения инкремента/декремента, так как операция увеличения/уменьшения уже произошла. С другой стороны, если мы возвращаем объект до выполнения инкремента/декремента, то сама операция увеличения/уменьшения объекта не выполнится.

    Я правильно понимаю, что по этому же принципу работает и цикл for (int i=0; i<b;++i), чтобы не создавать временный объект при написании i++?

  2. Богдан:

    А почему можно не указывать параметр int, тот самый, неявный, в постфиксной версии, если он там есть? Тоесть, что я хотел сказать), он как бы описан в сигнатуре финкции, а при вызове мы ничего не указываем. Почему компилятор не напишет что-то типа "too few arguments"?

    1. Ninedenon:

      Потому что это анонимный объект, который имеет область видимости r-value

  3. тоже юрий :):

    А почему нельзя в постфикс версии сделать так:

    1. Алексей:

      Можно, но сути это не меняет.

      1. Михаил:

        А как же создание временного объекта? В данном варианте ничего не создается и память просто так не тратится. Поправьте если не прав.

        1. meowpowww:

          В этом случае создается и возвращается анонимный объект.

  4. Владислав:

    Является ли эта "заглушка" в версии постфикса лишь заглушкой? Или постфикс-операторы инкремента и декремента могут работать с правым операндом?

    1. Ninedenon:

      Этот параметр является аонмным объекты который имеет продолжительность жизни r-value

  5. Артём:

    Объясните,пожалуйста,как при вызове постфиксного и префиксного инкремента компилятор понимает,где какой,если мы явно никак не указываем переменную типа int?

    1. Сергей:

      Если в параметрах функции написано int — значит постфикс, если нет — префикс. Компилятор различает по наличию аргументов в параметрах.

      1. Surprizze:

        Хотел уточнить, что если попробовать убрать параметры у версии постфикс и добавить в версию префикс — программа не скомпилируется. Компилятор ХОЧЕТ чтобы постфикс был с одним параметром:)

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

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