Урок №134. Перегрузка операторов через методы класса

  Юрий  | 

  |

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

 80103

 ǀ   7 

Перегрузка операторов через методы класса очень похожа на перегрузку операторов через дружественные функции. Но при перегрузке оператора через метод класса левым операндом становится неявный объект, на который указывает скрытый указатель *this.

Перегрузка операторов через методы классов

Вспомним, как выглядит перегрузка оператора через дружественную функцию:

Конвертация перегрузки через дружественную функцию в перегрузку через метод класса довольно-таки проста:

   Перегружаемый оператор определяется как метод класса, вместо дружественной функции (Dollars::operator+ вместо friend operator+).

   Левый параметр из функции перегрузки выбрасывается, вместо него — неявный объект, на который указывает указатель *this.

   Внутри тела функции перегрузки все ссылки на левый параметр могут быть удалены (например, dollars.m_dollars становится m_dollars, который неявно ссылается на текущий объект с помощью указателя *this).

Теперь та же перегрузка оператора +, но уже через метод класса:

Обратите внимание, использование оператора + не изменяется (в обоих случаях dollars1 + 3), но реализация отличается. Наша дружественная функция с двумя параметрами становится методом класса с одним параметром, причем левый параметр в перегрузке через дружественную функцию (&dollars), в перегрузке через метод класса становится неявным объектом, на который указывает указатель *this.

Рассмотрим детально, как обрабатывается выражение dollars1 + 3.

В перегрузке через дружественную функцию выражение dollars1 + 3 приводит к вызову функции operator+(dollars1, 3). Здесь есть два параметра.

В перегрузке через метод класса выражение dollars1 + 3 приводит к вызову dollars1.operator+(3). Обратите внимание, здесь уже один явный параметр, а dollars1 используется как префикс к operator+. Этот префикс компилятор неявно конвертирует в скрытый левый параметр, на который указывает указатель *this. Таким образом, dollars1.operator+(3) становится вызовом operator+(&dollars1, 3), что почти идентично перегрузке через дружественную функцию.

Итак, если мы можем перегрузить оператор через дружественную функцию или через метод класса, то что тогда выбрать? Прежде чем мы дадим ответ на этот вопрос, вам нужно узнать еще несколько деталей.

Не всё может быть перегружено через дружественные функции


Операторы присваивания (=), индекса ([]), вызова функции (()) и выбора члена (->) перегружаются через методы класса — это требование языка C++.

Не всё может быть перегружено через методы класса

На уроке о перегрузке операторов ввода и вывода мы перегружали оператор вывода << для класса Point через дружественную функцию:

Однако через метод класса перегрузить оператор << мы не сможем. Почему? Потому что при перегрузке через метод класса в качестве левого операнда используется текущий объект. В этом случае левым операндом является объект типа std::ostream. std::ostream является частью Cтандартной библиотеки C++. Мы не можем использовать std::ostream в качестве левого неявного параметра, на который бы указывал скрытый указатель *this, так как указатель *this может указывать только на текущий объект текущего класса, члены которого мы можем изменить, поэтому перегрузка оператора << должна осуществляться через дружественную функцию.

Аналогично, хотя мы можем перегрузить operator+(Dollars, int) через метод класса (как мы делали выше), мы не можем перегрузить operator+(int, Dollars) через метод класса, поскольку int теперь является левым операндом, на который указатель *this указывать не может.

Перегрузка операторов через методы класса не используется, если левый операнд не является классом (например, int), или это класс, который мы не можем изменить (например, std::ostream).

Какой способ перегрузки и когда следует использовать?


В большинстве случаев язык C++ позволяет выбирать самостоятельно способ перегрузки операторов.

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

При работе с бинарными операторами, которые изменяют левый операнд (например, operator+=()), обычно используется перегрузка через методы класса. В этих случаях левым операндом всегда является объект класса, на который указывает скрытый указатель *this.

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

Поэтому:

   Для операторов присваивания (=), индекса ([]), вызова функции (()) или выбора члена (->) используйте перегрузку через методы класса.

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

   Для перегрузки бинарных операторов, которые изменяют левый операнд (например, operator+=()) используйте перегрузку через методы класса, если это возможно.

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

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

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

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

  1. Surprizze:

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

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

      Пожалуйста)

  2. Ольга:

    Здравствуйте
    Как быть если нужно использовать выражение ?

    1. Артур:

      "Аналогично, хотя мы можем перегрузить operator+(Dollars, int) через метод класса (как мы делали выше), мы не можем перегрузить operator+(int, Dollars) через метод класса, поскольку int теперь является левым операндом, на который указатель *this указывать не может."
      Через метод класса нельзя. Используйте перегрузку через обычные/дружественные функции.

  3. Анастасия:

    "При перегрузке через метод класса в качестве левого операнда используется текущий объект."

    А что, если в качестве левого неявного параметра, на который бы указывал скрытый указатель *this, поставить нужный нам объект?

    Компилятор не ругается. Но, наверное, так лучше не делать.

    1. Анастасия:

      И в качестве параметров метода класса принимать только объект типа std::ostream

    2. Ninedenon:

      Вы забыли о правилах ассоциативности данный параметр читает выражение с лева на право таким образом компилятор будет интерпретировать выражение следующие образом (point<<) std::cout, и приведет к следующему ,ведь взаимодействие ключевого слова cout и оператора << превращает его в оператор вывода, в случае которой я описал выше он интерпретируется как оператор побитового сдвига

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

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