Реферат перегрузка операторов с

Обновлено: 07.07.2024

Перегрузка операторов в C++. Операторная функция. Ключевое слово operator . Перегрузка базовых арифметических операторов + , – , * , / . Примеры реализации встроенных операторных функций

Перед рассмотрением данной темы рекомендуется ознакомиться со следующей темой:

Содержание

  • 1. Что такое унарные и бинарные операторы?
  • 2. В чем состоит суть перегрузки операторов? Что такое операторная функция?
  • 3. Какими способами можно реализовать операторную функцию для заданного класса? Какие существуют разновидности операторных функций?
  • 4. Общая форма операторной функции, которая реализована в классе. Ключевое слово operator
  • 5. Пример перегрузки унарных и бинарных операторов для класса, который содержит одиночные данные. Операторная функция реализована внутри класса
  • 6. Пример перегрузки оператора ‘ * ‘, обрабатывающего класс, который содержит массив вещественных чисел. Операторная функция реализована внутри класса
  • 7. Пример суммирования двух массивов. Операторная функция operator+() размещается внутри класса
  • 8. Какие ограничения накладываются на перегруженные операторы?
  • 9. Какие операторы нельзя перегружать?
  • 10. Объекты каких типов может возвращать операторная функция? Примеры операторных функций, которые возвращают объекты разных типов
  • 11. Можно ли изменять значения операндов в операторной функции?
  • 12. Можно ли реализовать операторные функции в классе, которые перегружают одинаковый оператор, получают одинаковые параметры но возвращают разные значения?
  • 13. Можно ли реализовать две и более операторных функции в классе, которые перегружают одинаковый оператор и получают разные (отличные между собой) параметры?

Поиск на других ресурсах:

1. Что такое унарные и бинарные операторы?

Различают три основных вида операторов: унарные, бинарные и n -арные ( n >2).

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

Примеры унарных операторов:

Бинарные операторы – это операторы, которые для вычисления требуют двух операндов.

Пример. Ниже отображены фрагменты выражений с бинарными операторами + , – , % , *

n -арные операторы для вычислений требуют более двух операндов. В языке C++ есть тернарная операция ?: , которая для своей работы требует три операнда. Более подробно об этой тернарной операции ?: описывается здесь.

2. В чем состоит суть перегрузки операторов? Что такое операторная функция?

лучше вызвать более естественном способом:

В данном примере оператор ‘+’ считается перегруженным.

3. Какими способами можно реализовать операторную функцию для заданного класса? Какие существуют разновидности операторных функций?

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

4. Общая форма операторной функции, которая реализована в классе. Ключевое слово operator

Общая форма операторной функции, реализованной в классе, имеет следующий вид:

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

Объявляется класс Point , реализующий точку на координатной плоскости. В классе реализованы:

  • две внутренние переменные x , y , которые есть координатами точки;
  • два конструктора класса;
  • методы доступа к внутренним переменным класса GetX() , GetY() , SetX() , SetY() ;
  • две операторные функции operator+() и operator-() .

Как видно из вышеприведенного кода, операторная функция operator+() получает один параметр. Это значит, что эта функция реализует бинарный оператор ‘+’ . Этот параметр соответствует операнду, который размещается в правой части бинарного оператора ‘+’ . Операнд, который размещается в левой части оператора ‘+’ передается операторной функции неявно с помощью указателя this данного класса.
Вызов операторной функции осуществляет объект, который размещается в левой части оператора присваивания.
Демонстрация использования перегруженных операторов класса Point в другом методе:

В вышеприведенном коде, в операции суммирования ‘+’ объект P1 вызывает операторную функцию. То есть, фрагмент строки

Реализовать операторную функцию operator+() в классе можно и по другому

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

что значит: нет метода (конструктора) Point::Point() принимающего 2 аргумента.

6. Пример перегрузки оператора ‘*’ , обрабатывающего класс, который содержит массив вещественных чисел. Операторная функция реализована внутри класса

В примере реализуется операторная функция operator*() , которая умножает поэлементно значения внутренних массивов объектов класса ArrayFloat . Если размер массивов неодинаков, то умножается только то число элементов, которое есть минимальным между двумя размерами массивов.

Использование класса ArrayFloat в другом методе

7. Пример суммирования двух массивов. Операторная функция operator+() размещается внутри класса

Задан класс ArrayFloat , реализующий динамический массив чисел типа float . В классе реализованы:

  • внутренние переменные size , A , описывающие размер массива и сам массив;
  • два конструктора, инициализирующие начальными значениями элементы массива;
  • конструктор копирования;
  • методы доступа GetSize() , SetSize() , GetAi() , SetAi() , которые реализуют доступ к внутренним переменным массива с соответствующими операциями (выделение памяти, проверка на допустимые границы);
  • операторная функция operator=() , которая реализует копирование объектов;
  • операторная функция operator+() , которая реализует перегрузку оператора ‘+’ для массивов типа ArrayFloat . Операторная функция добавляет поэлементно значения массивов, которые являются операндами операции ‘+’ .
  • деструктор.

Ниже продемонстрировано использование класса ArrayFloat и операторной функции operator+() этого класса.

8. Какие ограничения накладываются на перегруженные операторы?

На использование перегруженных операторов накладываются следующие ограничения:

  • при перегрузке оператора нельзя изменить приоритет этого оператора;
  • нельзя изменить количество операндов оператора. Однако, в коде операторной функции можно один из параметров (операндов) не использовать;
  • нельзя перегружать операторы :: , . , * , ?: ;
  • нельзя вызвать операторную функцию с аргументами по умолчанию. Исключение – операторная функция вызова функции operator()() .
9. Какие операторы нельзя перегружать?

Нельзя перегружать следующие операторы:

  • :: – расширение области видимости;
  • . (точка) – доступ к члену структуры или класса;
  • * – доступ по указателю;
  • ?: – тернарная операция.
10. Объекты каких типов может возвращать операторная функция? Примеры операторных функций, которые возвращают объекты разных типов

Операторная функция может возвращать объекты любых типов. Наиболее часто операторная функция возвращает объект типа класса, в котором она реализованная или с которыми она работает.

Пример. Задан класс Complex , в котором перегружаются два оператора:

  • унарный оператор ‘+’ , возвращающий модуль комплексного числа (тип double );
  • бинарный оператор ‘+’ , возвращающий сумму комплексных чисел. Операторная функция возвращает объект типа Complex ;
  • бинарный оператор ‘+’ , который добавляет к комплексному числу некоторое вещественное число. В этом случае операторная функция получает входным параметром вещественное число и возвращает объект типа Complex .

Текст класса следующий:

Далее демонстрируется использование класса Complex и перегруженных операторных функций в некотором другому методе

11. Можно ли изменять значения операндов в операторной функции?

Да, можно. Однако такие действия не являются полезными с точки зрения здравого смысла. Так, например, операция умножения

не изменяет значения своих операндов 6 и 9. Результат равен 54. Если операторная функция operator*() будет изменять значения своих операндов, то это может привести к невидимым ошибкам в программах, поскольку программист по привычке, будет считать, что значения операндов есть неизменными.

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

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

Например. Нельзя в классе перегружать оператор ‘+’ так как показано ниже

Это правило касается любых функций класса.

13. Можно ли реализовать две и более операторных функции в классе, которые перегружают одинаковый оператор и получают разные (отличные между собой) параметры?

Да, можно. В п. 10 реализован класс Complex , в котором реализованы две операторные функции, которые перегружают оператор ‘+’ разными способами. Эти функции отличаются входящими параметрами.

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

Вложенные файлы: 1 файл

Лекция 28. Перегрузка операторов в языке С++. Общие понятия.doc

Основы алгоритмизации и программирования Лекция 28

1. Перегрузка операторов в языке С++. Общие понятия

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

В языке С++ реализован механизм перегрузки операторов. Такая перегрузка представляет собой еще один пример полиморфизма. Перегрузка операторов предоставляет возможность рассматривать операторы языка C++ сразу в нескольких смыслах. Фактически часть операторов языка C++ уже перегружены изначально. Например, оператор *, будучи примененной к адресу, дает значение, которое хранится по этому адресу. Однако, применяя оператор * к паре числовых значений, получаем произведение этих значений. Таким образом, в данном случае в языке C++ используется количество и типы операндов, чтобы решить, какое действие предпринять.

В отличие от функций для операторов языка С++ предусматриваются два дополнительных свойства, существенным образом влияющих на их функциональность: приоритет и ассоциативность. Действительно, встает вопрос как при записи понимать: a+b*c – как (a+b)*c или как a+(b*c)? Выражение a-b+c — это (a-b)+c или a-(b+c)?).

Приоритет (ранг или старшинство оператора) — формальное свойство оператора, влияющее на очередность его выполнения в выражении с несколькими различными операторами при отсутствии явного (с помощью скобок) указания на порядок их вычисления. Например, оператор умножения * обладает изначально более высоким приоритетом, чем оператор сложения +, а потому в выражении a+b*c будет получено сначала произведение b и c, а потом уже сумма.

Операторы могут иметь одинаковый приоритет, тогда они вычисляются по правилу ассоциативности, установленному для них. В программировании ассоциативностью операторов называют последовательность (очередность) их выполнения в случае, когда операторы имеют одинаковый приоритет и отсутствует явное (с помощью скобок) указание на очерёдность их выполнения. Причем различается левая ассоциативность, при которой вычисление выражения происходит слева–направо, и правая ассоциативность — справа–налево. Соответствующие операторы называют левоассоциативными и правоассоциативными. Например, в языке xBase СУБД Visual FoxPro большинство операторов имеет левую ассоциативность, в то время как возведение в степень правоассоциативно: x2 записывается по правилам xBase в виде x**2 или x^2.

В языках программирования (в том числе в языке С++) применяются два способа задания приоритетов операторов:

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

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

Поскольку для встроенных в язык операторов всегда предусматриваются приоритеты и ассоциативность, то возникает вопрос: какие приоритеты и ассоциативность будут иметь переопределённые версии этих операторов или, тем более, новые созданные программистом операторы?

Кроме того, имеются и другие “тонкие моменты”, которые могут требовать уточнения. Например, в языке С++ существуют две формы операторов инкремента (увеличения) и декремента (уменьшения) значения ++ и -- — префиксная и постфиксная, поведение которых различается. Как должны вести себя перегруженные версии таких операций? Различные языки по-разному решают приведённые вопросы. Так, в языке C++ приоритет и ассоциативность перегруженных версий операторов сохраняются такими же, как и у определённых в языке, а описания перегрузки префиксной и постфиксной формы операторов инкремента и декремента используют различные сигнатуры.

Итак. Перегрузка операторов — это возможность назначать новый смысл операторам при использовании их с определенным классом.

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

 Чтобы перегрузить оператор, необходимо определить класс, которому оператор будет назначен;

 Когда перегружаете оператор, то перегрузка действует только для класса, в котором он определяется. Если программа использует оператор с неклассовыми переменными (например, переменными типа int или float), используется стандартное определение оператора;

 Чтобы перегрузить оператор класса, необходимо использовать ключевое слово языка C++ operator для определения метода класса, который вызывается каждый раз, когда переменная класса использует оператор;

 В языке С++ разрешается перегружать следующие операторы:

 C++ не позволяет вашим программам перегружать оператор выбора элемента (.), оператор указателя на элемент (.*), оператор разрешения области видимости (::), условный оператор сравнения (?:), оператор sizeof().

2. Пример перегрузки унарных и бинарных операторов в языке С++

Рассмотрим пример программы, в которой демонстрируется механизм перегрузки операторов. В программе определяется класс int_Matrix для организации обработки целочисленной матрицы. Внутри этого класса реализован явный конструктор, которым предусматривается генерация значений матрицы в диапазоне от 0 до 9 с помощью ГСЗ. Определен метод OutPut() для вывода матрицы на экран. В классе реализована перегрузка бинарных операторов – и +, а также перегрузка унарного оператора –.

4. using namespace std;

5. class int_Matrix // класс для обработки целочисленной матрицы

7. int_Matrix(int, int, int, int); // прототип конструктора

8. void OutPut(int, int); // прототип метода вывода матрицы

9. //прототип бинарного оператора - (вычитания)

10. int_Matrix operator - (int_Matrix& F);

11. //прототип бинарного оператора + (сложения)

12. int_Matrix operator + (int_Matrix& D);

13. // прототип унарного оператора -

14. void operator - ();

17. int int_Matr[n][m]; // поле для размещения матрицы

19. // определение конструктора

20. int_Matrix::int_Matrix(int n, int m, int min, int max)

24. // определение метода OutPut()

25. void int_Matrix::OutPut(int n, int m)

35. // определение бинарного оператора +

36. int_Matrix int_Matrix::operator + (int_Matrix& D)

41. // определение унарного оператора -

42. void int_Matrix::operator - ()

50. cout можно перегружать только с помощью нестатической компонентной функции operator OP. Это гарантирует, что первыми операндами будут леводопустимые выражения.

operator Ключевое слово объявляет функцию, указывающую, какой operator означает при применении к экземплярам класса. Это дает оператору более одного значения — "перегружает" его. Компилятор различает разные значения оператора, проверяя типы его операндов.

Синтаксис

Комментарии

Функцию большинства встроенных операторов можно переопределить глобально или для отдельных классов. Перегруженные операторы реализуются в виде функции.

Имя перегруженного оператора — operator operator , где x — это оператор, как показано в следующей таблице. Например, для перегрузки оператора сложения необходимо определить функцию с именем operator +. Аналогично, чтобы перегрузить оператор сложения и присваивания, += Определите функцию с именем +=.

Переопределяемые операторы

Оператор Имя Тип
, Запятая Двоичные данные
! Логическое НЕ Унарный
!= Неравенство Двоичные данные
% Модуль Двоичные данные
%= Назначение модуля Двоичные данные
& Побитовое И Двоичные данные
& Взятие адреса Унарный
&& Логическое И Двоичные данные
= Назначение побитового И Двоичные данные
( ) Вызов функции
( ) Оператор приведения Унарный
* Умножение Двоичные данные
* Разыменование указателя Унарный
*= Присваивание умножения Двоичные данные
+ Сложение Двоичные данные
+ Унарный плюс Унарный
++ Шаг 1 Унарный
+= Присваивание сложения Двоичные данные
- Вычитание Двоичные данные
- Унарное отрицание Унарный
-- Уменьшить 1 Унарный
-= Присваивание вычитания Двоичные данные
- Выбор члена Двоичные данные
->* Выбор указателя на член Двоичные данные
/ Отдел Двоичные данные
/= Присваивание деления Двоичные данные
Больше чем Двоичные данные
= Больше или равно Двоичные данные
>> Сдвиг вправо Двоичные данные
= Сдвиг вправо и присваивание Двоичные данные
[ ] Индекс массива
^ Исключающее ИЛИ Двоичные данные
^= Исключающее ИЛИ/присваивание Двоичные данные
| Побитовое ИЛИ Двоичные данные
|= Назначение побитового включающего ИЛИ Двоичные данные
|| Логическое ИЛИ Двоичные данные
~ Дополнение до единицы Унарный
delete Удалить
new Оператор new
операторы преобразования операторы преобразования Унарный

Существует 1 две версии унарных операторов инкремента и декремента: добавочное и инкрементное.

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

В прошлой части мы рассмотрели основные аспекты использования перегрузки операторов. В этом материалы вашему вниманию будут представлены перегружаемые операторы C++. Для каждого раздела характерна семантика, т.е. ожидаемое поведение. Кроме того, будут показаны типичные способы объявления и реализации операторов.

В примерах кода X означает пользовательский тип, для которого реализован оператор. T — это необязательный тип, пользовательский либо встроенный. Параметры бинарного оператора будут называться lhs и rhs . Если оператор будет объявлен как метод класса, у его объявления будет префикс X:: .

operator=

  • Определение справа налево: в отличие от большинства операторов, operator= правоассоциативен, т.е. a = b = c означает a = (b = c) .

Копирование

  • Семантика: присваивание a = b . Значение или состояние b передаётся a . Кроме того, возвращается ссылка на a . Это позволяет создавать цепочки вида c = a = b .
  • Типичное объявление: X& X::operator= (X const& rhs) . Возможны другие типы аргументов, но используется это нечасто.
  • Типичная реализация:

Перемещение (начиная с C++11)

  • Семантика: присваивание a = temporary() . Значение или состояние правой величины присваивается a путём перемещения содержимого. Возвращается ссылка на a .
  • Типичные объявление и реализация:
  • Сгенерированный компилятором operator= : компилятор может создать только два вида этого оператора. Если же оператор не объявлен в классе, компилятор пытается создать публичные операторы копирования и перемещения. Начиная с C++11 компилятор может создавать оператор по умолчанию:

operator+, -, *, /, %

  • Семантика: операции сложения, вычитания, умножения, деления, деления с остатком. Возвращается новый объект с результирующим значением.
  • Типичные объявление и реализация:

Обычно, если существует operator+ , имеет смысл также перегрузить и operator+= для того, чтобы использовать запись a += b вместо a = a + b . Если же operator+= не перегружен, реализация будет выглядеть примерно так:

Унарные operator+, —

  • Семантика: положительный или отрицательный знак. operator+ обычно ничего не делает и поэтому почти не используется. operator- возвращает аргумент с противоположным знаком.
  • Типичные объявление и реализация:

operator >

  • Семантика: во встроенных типах операторы используются для битового сдвига левого аргумента. Перегрузка этих операторов с именно такой семантикой встречается редко, на ум приходит лишь std::bitset . Однако, для работы с потоками была введена новая семантика, и перегрузка операторов ввода/вывода весьма распространена.
  • Типичные объявление и реализация: поскольку в стандартные классы iostream добавлять методы нельзя, операторы сдвига для определённых вами классов нужно перегружать в виде свободных функций:

Кроме того, тип левого операнда может быть любым классом, которые должен вести себя как объект ввода/вывода, то есть правый операнд может быть и встроенного типа.

Бинарные operator&, |, ^

operator+=, -=, *=, /=, %=

  • Семантика: a += b обычно означает то же, что и a = a + b . Поведение остальных операторов аналогично.
  • Типичные определение и реализация: поскольку операция изменяет левый операнд, скрытое приведение типов нежелательно. Поэтому эти операторы должны быть перегружены как методы класса.

operator&=, |=, ^=, >=

  • Семантика: аналогична operator+= , но для логических операций. Эти операторы перегружаются так же редко, как и operator| и т.д. operator и operator>>= не используются для операций ввода/вывода, поскольку operator и operator>> уже изменяют левый аргумент.

operator==, !=

  • Семантика: проверка на равенство/неравенство. Смысл равенства очень сильно зависит от класса. В любом случае, учитывайте следующие свойства равенств:
    1. Рефлексивность, т.е. a == a .
    2. Симметричность, т.е. если a == b , то b == a .
    3. Транзитивность, т.е. если a == b и b == c , то a == c .
  • Типичные объявление и реализация:

operator , >=

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

Реализация operator> с использованием operator или наоборот обеспечивает однозначное определение. operator может быть реализован по-разному, в зависимости от ситуации. В частности, при отношении строго порядка operator== можно реализовать лишь через operator :

operator++, —

  • Семантика: a++ (постинкремент) увеличивает значение на 1 и возвращает старое значение. ++a (преинкремент) возвращает новое значение. С декрементом operator-- все аналогично.
  • Типичные объявление и реализация:

operator()

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

operator[]

  • Семантика: доступ к элементам массива или контейнера, например, в std::vector , std::map , std::array .
  • Объявление: тип параметра может быть любым. Тип возвращаемого значения обычно является ссылкой на то, что хранится в контейнере. Часто оператор перегружается в двух версиях, константной и неконстантной:

operator!

  • Семантика: отрицание в логическом смысле.
  • Типичные объявление и реализация:

explicit operator bool

  • Семантика: использования в логическом контексте. Чаще всего используется с умными указателями.
  • Реализация:

operator&&, ||

Унарный operator*

  • Семантика: разыменовывание указателя. Обычно перегружается для классов с умными указателями и итераторами. Возвращает ссылку на то, куда указывает объект.
  • Типичные объявление и реализация:

operator->

  • Семантика: доступ к полю по указателю. Как и предыдущий, этот оператор перегружается для использования с умными указателями и итераторами. Если в коде встречается оператор -> , компилятор перенаправляет вызовы на operator-> , если возвращается результат пользовательского типа.
  • Usual implementation:

operator->*

  • Семантика: доступ к указателю-на-поле по указателю. Оператор берёт указатель на поле и применяет его к тому, на что указывает *this , то есть objPtr->*memPtr — это то же самое, что и (*objPtr).*memPtr . Используется очень редко.
  • Возможная реализация:

Унарный operator&

  • Семантика: адресный оператор. Этот оператор перегружают очень редко.

operator,

operator~

  • Семантика: оператор побитовой инверсии. Один из наиболее редко используемых операторов.

Операторы приведения типов

  • Семантика: позволяет скрытое или явное приведение объектов класса к другим типам.
  • Объявление:

operator new, new[], delete, delete[]

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

Заключение

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

Читайте также: