Автор работы: Пользователь скрыл имя, 30 Мая 2012 в 00:07, лекция
Ранее мы познакомились с механизмом перегрузки функций, когда имеется возможность определять несколько функций, имеющих одно и то же имя при условии, что у них разные сигнатуры (списки аргументов). На основе перегрузки функций в языке С++ реализуется функциональный полиморфизм.
Основы алгоритмизации и программирования Лекция 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 для определения метода класса, который вызывается каждый раз, когда переменная класса использует оператор;
В языке С++ разрешается перегружать следующие операторы:
+ - * / % ^ & | ~ !
= < > += -= *= /= %= ^= &=
|= << >> >>= <<= == != <= >= &&
|| ++ -- ->* , -> [] () new delete
C++ не позволяет вашим программам перегружать оператор выбора элемента (.), оператор указателя на элемент (.*), оператор разрешения области видимости (::), условный оператор сравнения (?:), оператор sizeof().
2. Пример перегрузки унарных и бинарных операторов в языке С++
Рассмотрим пример программы, в которой демонстрируется механизм перегрузки операторов. В программе определяется класс int_Matrix для организации обработки целочисленной матрицы. Внутри этого класса реализован явный конструктор, которым предусматривается генерация значений матрицы в диапазоне от 0 до 9 с помощью ГСЗ. Определен метод OutPut() для вывода матрицы на экран. В классе реализована перегрузка бинарных операторов – и +, а также перегрузка унарного оператора –.
1. #include <iostream>
2. #include <stdlib.h>
3. #include <time.h>
4. using namespace std;
5. class int_Matrix // класс для обработки целочисленной матрицы
6. { public:
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 - ();
15. private:
16. enum {n=10, m=12};
17. int int_Matr[n][m]; // поле для размещения матрицы
18. };
19. // определение конструктора
20. int_Matrix::int_Matrix(int n, int m, int min, int max)
21. { for(int i=0; i<n; i++)
22. for(int j=0; j<m; j++)
23. int_Matr[i][j]=rand()%(max-
24. // определение метода OutPut()
25. void int_Matrix::OutPut(int n, int m)
26. { for(int i=0; i<n; i++) {
27. for(int j=0; j<m; j++) cout << int_Matr[i][j] << ' '; 28. cout << endl; } }
29. // определение бинарного оператора -
30. int_Matrix int_Matrix::operator - (int_Matrix& F)
31. { for(int i=0; i<n; i++)
32. for(int j=0; j<m; j++)
33. F.int_Matr[i][j]=int_Matr[i][
34. return F; }
35. // определение бинарного оператора +
36. int_Matrix int_Matrix::operator + (int_Matrix& D)
37. { for(int i=0; i<n; i++)
38. for(int j=0; j<m; j++)
39. D.int_Matr[i][j]=int_Matr[i][
40. return D; }
41. // определение унарного оператора -
42. void int_Matrix::operator - ()
43. { for(int i=0; i<n; i++)
44. for(int j=0; j<m; j++) int_Matr[i][j]=-int_Matr[i][j]
45. int main()
46. { int n, m, min=0, max=9;
47. srand((unsigned)time(NULL));
48. cout << "Vvedite kol-vo strok (<=10) i stolbtsov (<=12): ";
49. cin >> n >> m;
50. cout << "\nMatritsa A:";
51. int_Matrix A(n,m,min,max); cout << endl; A.OutPut(n,m);
52. cout << "\nMatritsa B:";
53. int_Matrix B(n,m,min,max); cout << endl; B.OutPut(n,m);
54. int_Matrix C(n,m,min,max);
55. C=B; cout << "\nMatritsa A-B:\n"; C=A-C; C.OutPut(n,m);
56. C=B; cout << "\nMatritsa A+B:\n"; C=A+C; C.OutPut(n,m);
57. cout << "\nMatritsa -B:\n"; -B; B.OutPut(n,m);
58. return 0;
59. }
В строках с 5 по 18 включительно представлено определение класса int_Matrix для организации обработки целочисленной матрицы.
В строках с 29 по 34 и строках с 35 по 40 определены соответственно бинарные операторы – и +. Например, на вход оператора + (см. стр. 35) подается объект D класса int_Matrix. На основе вложенных циклов реализована операция поэлементного сложения матриц объекта D и объекта, стоящего в выражении сложения слева от +. В результате формируется матрица объекта D, которая и возвращается в качестве результата.
В строках с 42 по 44 определен унарный оператор –. На основе вложенных циклов реализована операция поэлементного отрицания значений элементов матрицы, для которой выполняется эта унарная операция.
Функция main() определена в строках с 45 по 59 включительно. В начале определяются переменные n и m для размещения в них значений числа соответственно строк и столбцов матрицы. Переменные min и max используются для определения границ диапазона, в котором случайным образом формируются значения элементов матриц A,B,C. Отметим, что матрица C используется как промежуточная для корректного выполнения операций A+B, A–B.
В строке 51 создается объект А, внутри которого с помощью ГСЗ формируется матрица целочисленных значений и ее значения выводятся на экран методом OutPut().
В строке 53 создается объект B, внутри которого с помощью ГСЗ формируется матрица целочисленных значений и ее значения выводятся на экран методом OutPut().
В строке 54 создается объект C, внутри которого с помощью ГСЗ формируется матрица целочисленных значений.
В строках 55 и 56 соответственно реализованы операции A–B и A+B, причем матрица C играет важную роль, поскольку в процессе реализации указанных операций необходимо, с одной стороны, сохранить исходные значения матриц A и B, с другой стороны, формировать промежуточные результаты.
3. Основные правила перегрузки операторов в языке С++
1) Нельзя вводить собственные обозначения для операторов, не совпадающие со стандартными операторами языка С++.
2) Не все операторы языка С++ могут быть перегружены. Так нельзя перегрузить:
. – прямой выбор элемента
.* – обращение к элементу через указатель на него
? : – условная операция
:: – операция указания области видимости
sizeof, # и ## – препроцессорные операции.
3) Каждый оператор в языке имеет определенное число операндов, свой приоритет и ассоциативность. Все эти правила сохраняются и при перегрузке, а изменить их нельзя.
4) Любой унарный оператор OP определяется двумя способами: либо как компонентная функция без параметров, либо как глобальная (возможно дружественная) функция с одним параметром. Выражение OPz означает в первом случае вызов z.operator OP(), во втором - вызов operator OP(z).
5) Любой бинарный оператор OP определяется также двумя способами: либо как компонентная функция с одним параметром, либо как глобальная (возможно дружественная) функция с двумя параметрами. В первом случае x OP y означает вызов x.operator OP(y), во втором – вызов operator OP(x, y).
6) Перегруженный оператор не может иметь аргументы по умолчанию.
7) В языке С++ установлена идентичность некоторых операторов, например, ++z – это тоже, что и z += 1. Эта идентичность теряется для перегруженных операторов.
8) Функцию operator можно вызвать по ее имени, например, z = operator * (x, y) или z = x.operator *(y). В первом случае вызывается глобальная функция, во втором – компонентная функция класса X, и x – это объект класса X. Однако, чаще всего функция operator вызывается косвенно, например, z = x * y.
9) За исключением перегрузки операторов new и delete функция operator должна быть либо нестатической компонентной функцией, либо иметь как минимум один аргумент (операнд) типа "класс" или "ссылка на класс" (если это глобальная функция).
10) Операторы =, [ ], –> можно перегружать только с помощью нестатической компонентной функции operator OP. Это гарантирует, что первыми операндами будут леводопустимые выражения.
Информация о работе Перегрузка операторов в языке С++. Общие понятия