- •Учебное пособие
- •Введение
- •Объектно-ориентированный подход
- •Объектно-ориентированное программирование Абстрактные типы данных
- •Базовые принципы объектно-ориентированного программирования
- •Простейший ввод и вывод
- •Объект cout
- •Манипуляторы hex и oct
- •Другие манипуляторы
- •Объект cin
- •Операторы для динамического выделения и освобождения памяти (new и delete)
- •Базовые конструкции объектно-ориентированных программ Объекты
- •Понятие класса
- •Конструктор копирования
- •Конструктор explicit
- •Указатели на компоненты класса
- •Встроенные функции (спецификатор inline)
- •Организация внешнего доступа к локальным компонентам класса (спецификатор friend)
- •Вложенные классы
- •Static-члены (данные) класса
- •Указатель this
- •Компоненты-функции static и const
- •Proxi-классы
- •Параметры ссылки
- •Независимые ссылки
- •Практические приемы ограничения числа объектов класса
- •Наследование (производные классы)
- •Конструкторы и деструкторы при наследовании
- •Виртуальные функции
- •Абстрактные классы
- •Виртуальные деструкторы
- •Множественное наследование
- •Виртуальное наследование
- •Перегрузка функций
- •Перегрузка операторов
- •Перегрузка бинарного оператора
- •Перегрузка унарного оператора
- •Дружественная функция operator
- •Перегрузка оператора []
- •Перегрузка оператора ()
- •Перегрузка операторов new и delete
- •Преобразование типа
- •Явные преобразования типов
- •Преобразования типов, определенных в программе
- •Шаблоны Параметризированные классы
- •Передача в шаблон класса дополнительных параметров
- •Шаблоны функций
- •Совместное использование шаблонов и наследования
- •Шаблоны класса и friend
- •Некоторые примеры использования шаблона класса Реализация smart-указателя
- •Классы поддерживающие транзакции
- •Задание значений параметров класса по умолчанию
- •Пространства имен
- •Ключевое слово using как директива
- •Ключевое слово using как объявление
- •Псевдоним пространства имен
- •Организация ввода-вывода
- •Состояние потока
- •Строковые потоки
- •Организация работы с файлами
- •Организация файла последовательного доступа
- •Создание файла произвольного доступа
- •Основные функции классов ios, istream, ostream
- •Основы обработки исключительных ситуаций
- •Перенаправление исключительных ситуаций
- •Исключительная ситуация, генерируемая оператором new
- •Генерация исключений в конструкторах
- •Задание собственной функции завершения
- •Спецификации исключительных ситуаций
- •Задание собственного неожиданного обработчика
- •Иерархия исключений стандартной библиотеки
- •Стандартная библиотека шаблонов (stl) Общее понятие о контейнере
- •Общее понятие об итераторе
- •Категории итераторов
- •Основные итераторы
- •Вспомогательные итераторы
- •Операции с итераторами
- •Контейнерные классы Контейнеры последовательностей
- •Контейнер последовательностей vector
- •Контейнер последовательностей list
- •Контейнер последовательностей deque
- •Ассоциативные контейнеры
- •Ассоциативный контейнер multiset
- •Ассоциативный контейнер set
- •Ассоциативный контейнер multimap
- •Ассоциативный контейнер map
- •Адаптеры контейнеров
- •Адаптеры stack
- •Адаптеры queue
- •Адаптеры priority_queue
- •Пассивные и активные итераторы
- •Алгоритмы
- •Алгоритмы сортировки sort, partial_sort, sort_heap
- •Алгоритмы поиска find, find_if, find_end, binary_search
- •Алгоритмы fill, fill_n, generate и generate_n
- •Алгоритмы equal, mismatch и lexicographical_compare
- •Математические алгоритмы
- •Алгоритмы copy, copy_backward, merge, unique и reverse
- •Алгоритмы работы с множествами
- •Алгоритмы swap, iter_swap и swap_ranges
- •Примеры реализации контейнерных классов Связанные списки
- •Реализация односвязного списка
- •Реализация двусвязного списка
- •Реализация двоичного дерева
- •Литература
- •Вопросы по курсу ооп
- •220013, Минск, п.Бровки, 6.
Перегрузка бинарного оператора
Функция operator для перегрузки (доопределения) бинарных операторов может быть описана двумя способами:
как компонента-функция класса с одним аргументом;
как глобальная функция (функция, описанная вне класса) с двумя аргументами.
При перегрузке бинарного оператора # выражение a#b может быть представлено при первом способе как a.operator#(b) или как operator #(a,b) при втором способе перегрузки.
Рассмотрим простой пример переопределения операторов *, = , > и == по отношению к объекту, содержащему декартовы координаты точки на плоскости. В примере использован первый способ перегрузки.
#include "iostream.h"
class dek_koord
{ int x,y; // декартовы координаты точки
public:
dek_koord(){};
dek_koord(int X,int Y): x(X),y(Y) {}
dek_koord operator*(const dek_koord);
dek_koord operator=(const dek_koord);
dek_koord operator>(const dek_koord);
int operator ==(const dek_koord);
void see();
};
dek_koord dek_koord::operator*(dek_koord a) // перегрузка операции *
{ dek_koord tmp; // локальный объект
tmp.x=x*a.x;
tmp.y= y*a.y;
return tmp;
}
dek_koord dek_koord::operator =(const dek_koord a)
{ x=a.x; // перегрузка операции =
y=a.y;
return *this;
}
dek_koord dek_koord::operator >(const dek_koord a)
{ if (x<a.x) x=a.x; // перегрузка операции >
if (y<a.y) y=a.y;
return *this;
}
int dek_koord::operator ==(const dek_koord a) // перегрузка операции ==
{ if (x==a.x && y==a.y) return 0; // 0 – координаты равны
if (x>a.x && y>a.y) return 1; //
if (x<a.x && y<a.y) return -1; //
else return 2; // неопределенность
}
void dek_koord::see() // функция просмотра содержимого объекта
{ cout << "координата х = " << x << endl;
cout << "координата y = " << y << endl;
}
void main()
{ dek_koord A(1,2), B(3,4), C;
int i;
A.see();
B.see();
C=A*B; // вначале перегрузка операции * затем =
C.see();
C=A>B; // компоненты объекта С принимают значение max от А и В
C.see();
i=A==B; // i получает значение сравненияA==B (-1,0,1,2….)
// cout << A==B << endl; // ошибка
// error binary '<<' : no operator defined which takes a right-hand operand
// of type 'class dek_koord' (or there is no acceptable conversion)
cout << (A==B) << endl; // верно
}
Результат работы программы:
координата х = 1
координата y = 2
координата х = 3
координата y = 4
координата х = 3
координата y = 8
координата х = 3
координата y = 4
В приведенной выше программе функцию перегрузки оператора * можно изменить, например, следующим образом:
dek_koord &dek_koord::operator*(const dek_koord &a)
{ x*=a.x;
y*=a.y;
return *this;
}
В этом примере функция operator* в качестве параметра получает ссылку на объект, стоящий в правой части выражения А*В, то есть на В. Ссылка – это второе имя (псевдоним) для одного и того же объекта. Более подробно ссылки будут рассмотрены позже. Функция operator* при вызове получает скрытый указатель на объект А и модифицирует неявные параметры (компоненты-данные объекта А – х и у). Возвращается значение по адресу this, то есть объект А. Возвращать ссылку на объект необходимо для реализации выражения вида A*B*C.
Следует отметить, что если в описании класса dek_koord присутствуют объявления двух функций перегрузки операции *:
class dek_koord
{ . . .
dek_koord operator*(const dek_koord );
dek_koord &operator*(const dek_koord &);
. . .
};
то возникает ошибка. Аналогично ошибка будет, если одна из функций является компонентой класса, а другая глобальной функцией.
Если возвращаемое значение функции operator является ссылкой, то в этом случае возвращаемое значение не может быть автоматической или статической локальной переменной.
Рассмотрим фрагмент программы, в которой функция operator* является глобальной.
class dek_koord
{ int x,y; // декартовы координаты точки
public:
. . .
int read_x(); // возвращает компоненту x
int read_y(); // возвращает компоненту y
void write_x(int); // модифицирует компоненту x
void write_y(int); // модифицирует компоненту y
. . .
};
int dek_koord::read_x(){return x;}
int dek_koord::read_y(){return y;}
void dek_koord::write_x(int a){x=a;}
void dek_koord::write_y(int a){y=a;}
dek_koord operator*(dek_koord a,dek_koord b) // перегрузка операции *
{ dek_koord tmp; // функция operator - глобальная
tmp.write_x(a.read_x()*b.read_x());
tmp.write_y(a.read_y()*b.read_y());
return tmp;
}
В глобальной функции operator* доступ к private данным локального объекта tmp возможен через public функции этого объекта, либо данные класса должны иметь атрибут public, что не отвечает принципу инкапсуляции. Кроме того, если функция operator является friend-функцией некоторого класса, то она имеет доступ к private компонентам этого класса. Это будет рассмотрено несколько позже.
Ниже приведен пример еще одной программы перегрузки оператора ”-“ для использования его при вычитании из одной строки другой.
#include <iostream.h>
#include <string.h>
class String
{ char str[80]; // локальная компонента
public: // глобальные компоненты
void init (char *s); // функция инициализации
int operator - (String s_new); // прототип функции operator
} my_string1, my_string2; // описание двух объектов класса String
void String::init (char *s) // функция обеспечивает копирование
// строки аргумента(s) в строку-компоненту
{ strcpy(str,s);} // (str) класса String
int String::operator - (String s_new) // перегрузка оператора – (вычитания
// строк)
{ for (int i=0; str[i]==s_new.str[i]; i++)
if (!str[i]) return 0;
return str[i] - s_new.str[i];
}
void main(void)
{ char s1[51], s2[51];
cout <<"Введите первую строку не более 80 символов:" <<endl;
cin >>s1;
cout<<" Введите вторую строку не более 80 символов "<<endl;
cin>>s2;
my_string1.init(s1); //инициализация объекта my_string1
my_string2.init(s2); //инициализация объекта my_string2
cout <<"\nString1 - String2 = "; // вывод на экран разности двух строк
cout << my_string1 - my_string2 << endl;
}
Результат работы программы:
Введите первую строку не более 80 символов:
overload
Введите вторую строку не более 80 символов
function
String1 – String2 = 9
При перегрузке бинарного оператора с использованием компоненты-функции ей передается в качестве параметра только один аргумент. Второй аргумент получается посредством использования неявного указателя this на объект, компоненты которого модифицируются.
