Lab02 - Кафедра АСУ - Белорусско

advertisement
Министерство образования Республики Беларусь
Министерство образования и науки Российской Федерации
ГУВПО “Белорусско-Российский университет”
Кафедра “Автоматизированные
cистемы управления”
Дисциплина “Объектно-ориентированное программирование
и проектирование”
Лабораторная работа № 2
Перегрузка операций
Время выполнения работы – 4 часа
2014
2
1
Цель работы
Получение навыков в разработке программ с использованием перегрузки операторов.
2 Техническое обеспечение
1.1
1.2
1.3
1.4
Персональная ЭВМ с процессором Pentium 2 и более поздних моделей.
Клавиатура.
Дисплей.
Печатающее устройство.
3 Программное обеспечение
3.1 Операционная система Windows 2000 и более поздние версии.
3.2 Система программирования Visual C++ версия 6.0 и более поздние версии.
4 Постановка задачи
Выполнить задание согласно заданному варианту. Варианты задач приведены в
конце методических указаний в разделе 7.
5 Содержание отчета
5.1 Тема и цель работы.
5.2 Текст программы.
5.3 Результаты выполнения программы.
6 Общие сведения
6.1 Основы перегрузки операторов
В языке С++ могут быть перегружены следующие операторы:
+

*
/
%
^
|
~
!
=
<
>
=
*=
/=
%=
^=
&=
<<
>>
<<=
>>=
==
!=
>=
&&
||
++

>*
>
[]
()
new
new[]
delete
&
+=
|=
<=
,
delete[]
Следующие операторы не могут быть перегружены:
:: (разрешение области видимости);
. (выбор элемента);
.* (выбор элемента через указатель на элемент).
Правым операндом у них является не значение, а имя, и они предоставляют основные
средства доступа к элементам. Разрешение их перегрузки привело бы к очень тонким
ошибкам. Тернарный условный оператор ?: также не перегружается.
Для перегрузки оператора создается операторная функция. Чаще всего операторная
функция является членом класса или дружественной классу, для которого она определена.
Основная форма записи операторной функции – члена класса имеет вид:
возвращаемый_тип имя_класса :: operator# (список_параметров)
{
// выполняемая операция
}
Чаще всего типом возвращаемого значения операторной функции является класс, для которого она определена, хотя операторная функция может возвращать данные любого типа.
В представленной общей форме операторной функции вместо знака # надо подставить пе-
3
регружаемый оператор. Например, если перегружается оператор +, то у функции должно
быть имя operator+. Содержание списка список_параметров зависит от реализации операторной функции и от типа перегружаемого оператора.
Следует запомнить два важных ограничения на перегрузку операторов. Во-первых,
нельзя менять приоритет операторов. Во-вторых, нельзя менять число операндов оператора.
Операторные функции, за исключением оператора =, наследуются производным
классом.
Хотя допустимо иметь операторную функцию для реализации любого действия 
связанного или нет с традиционным употреблением оператора – лучше, если действия перегружаемых операторов остаются в сфере их традиционного использования. При создании перегружаемых операторов, для которых этот принцип не поддерживается, имеется
риск существенного снижения читабельности программы.
И последнее, операторная функция не может иметь параметров по умолчанию.
Действие перегруженного оператора по отношению к тому классу, для которого он
определен, не обязательно должно соответствовать каким-либо образом действию этого
оператора для встроенных типов С++. Однако, исходя из стремления сделать код более
легко читаемым и хорошо структурированным, желательно чтобы перегруженные операторы соответствовали там, где это возможно, смыслу исходных операторов.
Имеются некоторые ограничения на перегрузку операторов. Во-первых, нельзя изменить приоритет оператора. Во-вторых, нельзя изменить число операндов оператора.
В-третьих, все перегруженные операторы, за исключением оператора присваивания =,
наследуются любым производным классом. Каждый класс обязан явным образом определить свой собственный перегруженный оператор =, если он требуется для каких-то целей.
6.2 Перегрузка бинарных операторов
Когда операторная функция – элемент класса перегружает бинарный оператор, у
функции бывает только один параметр. Этот параметр получит тот объект, который расположен справа от оператора. Объект слева генерирует вызов операторной функции и передается неявно с помощью указателя this.
Чтобы увидеть, как работает перегрузка бинарных операторов, начнем с простого
примера. В нем создается класс three_d, содержащий координаты объекта в трехмерном
пространстве. Следующая программа перегружает операторы + и = для класса three_d:
#include <iostream.h>
class three_d {
int x, y, x;
public:
three_d operator+(three_d t);
three_d operator=(three_d t);
void show();
void assign(int mx, int my, int mz);
};
// перегрузка +
three_d three_d :: operator+(three_d t)
{
three_d temp;
temp.x = x + t.x;
temp.y = y + t.y;
temp.z = z + t.z;
return temp;
4
}
// перегрузка =
three_d three_d :: operator=(three_d t)
{
x = t.x;
y = t.y;
z = t.z;
return *this;
}
// вывод координат x, y, z
void three_d :: show()
{
cout << x << ‘ ‘;
cout << y << ‘ ‘;
cout << z << ‘\n’;
}
// присвоение координат
void three_d assign(int mx, int my, int mz)
{
x = mx;
y = my;
z = mz;
}
int main()
{
three_d a, b, c;
a.assign(1, 2, 3);
b.assign(10, 10, 10);
a.show();
b.show();
c = a + b; // сложение а и b
c.show();
c = a + b + c; // сложение a, b и с
c.show();
c = b = a; // множественное присваивание
c.show();
b.show();
return 0;
}
Эта программа выводит на экран следующие данные:
1 2 3
10 10 10
11 12 13
22 24 26
5
1 2 3
1 2 3
Чтобы понять, как работает перегрузка операторов, проанализируем предыдущую
программу, начиная с перегруженного оператора +. Когда два объекта типа three_d подвергаются воздействию оператора +, значения их соответствующих координат складываются, как это показано в функции operator+(), ассоциированной с данным классом. Функция не модифицирует значения операндов. Вместо этого она возвращает объект типа
three_d, содержащий результаты выполнения операции.
Другим ключевым моментом перезагрузки оператора сложения служит то, что он
возвращает объект типа three_d, что позволяет использовать оператор + в более сложных
выражениях, таких как a + b + c. Здесь a + b создает результат типа three_d. Это значение затем прибавляется к c. Если бы значением суммы a + b было значение другого типа,
то мы не могли бы его затем прибавить к с.
В противоположность оператору +, оператор присваивания = модифицирует свои
аргументы. (В этом, кроме всего прочего, и заключается смысл присваивания). Поскольку
функция operator=() вызывается объектом, стоящим слева от знака равенства, то именно
этот объект модифицируется при выполнении операции при выполнении операции присваивания. Однако даже оператор присваивания обязан возвращать значения, поскольку
как в С++, так и в С оператор присваивания порождает величину, стоящую с правой стороны равенства. Так для того, чтобы выражение следующего вида
a = b = c = d;
было допустимым, необходимо, чтобы operator=() возвращая объект, на который указывает указатель this и который будет объектом, стоящим с левой стороны оператора присваивания. Если сделать таким образом, то можно выполнить множественное присваивание.
6.3 Перегрузка унарных операторов
Можно перегрузить унарные операторы, такие, например, как ++ или . Как уже
говорилось ранее, при перегрузке унарного оператора с использованием функцииэлемента, эта функция-элемент не имеет аргументов. Вместо этого операция выполняется
над объектом, осуществляющим вызов функции-оператора, путем неявной передачи указателя this. В качестве примера рассмотрена расширенная версия предыдущей программы,
в которой определяется оператор-инкремент для объекта типа three_d:
#include <iostream.h>
class three_d {
int x, y, x;
public:
three_d operator+(three_d ob2);
three_d operator=(three_d ob2);
three_d operator++( );
void show();
void assign(int mx, int my, int mz);
};
// перегрузка +
three_d three_d :: operator+(three_d ob2)
{
three_d temp;
temp.x = x + ob2.x;
temp.y = y + ob2.y;
6
temp.z = z + ob2.z;
return temp;
}
// перегрузка =
three_d three_d :: operator=(three_d ob2)
{
x = ob2.x;
y = ob2.y;
z = ob2.z;
return *this;
}
// перегрузка унарного ++
three_d three_d :: operator++( )
{
x++;
y++;
z++;
return *this;
}
// вывод координат x, y, z
void three_d :: show()
{
cout << x << ‘ ‘;
cout << y << ‘ ‘;
cout << z << ‘\n’;
}
// присвоение координат
void three_d assign(int mx, int my, int mz)
{
x = mx;
y = my;
z = mz;
}
int main()
{
three_d a, b, c;
a.assign(1, 2, 3);
b.assign(10, 10, 10);
a.show();
b.show();
c = a + b; // сложение а и b
c.show();
c = a + b + c; // сложение a, b и с
c.show();
7
c = b = a; // множественное присваивание
c.show();
b.show();
++c;
c.show();
return 0;
}
В ранних версиях С++ было невозможно определить, предшествует или следует за
операндом перегруженный оператор ++ или . Например, для объекта ob следующие две
инструкции были идентичными:
ob++;
++ob;
Однако более поздние версии С++ позволяют различать префиксную и постфиксную
форму операторов инкремента и декремента. Для этого программа должна определить две
версии функции operator++(). Одна из них должна быть такой, как показано в предыдущей программе. Другая объявляется следующим образом:
loc operator++(int x);
Если ++ предшествует операнду, то вызывается функция operator++(). Если же ++ следует
за операндом, то тогда вызывается operator++(int x), где х принимает значение 0.
6.4 Дружественная функция-оператор
Функция-оператор может быть другом класса, а не только его элементом. Поскольку функции-друзья не являются элементами класса, они не могут иметь неявный аргумент
this. Поэтому при использовании дружественной функции-оператора оба операнда передаются функции при перегрузке бинарных операторов, а при перегрузке унарных операторов передается один операнд. Следующие операторы не могут использовать перегрузку
с помощью дружественных функций: :=, (), [], ->. В качестве примера рассмотрим модифицированную версию предыдущей программы, в которой оператор ++ перегружен с помощью дружественной функции:
#include <iostream.h>
class three_d {
int x, y, x;
public:
friend three_d operator+(three_d ob1, three_d ob2);
three_d operator=(three_d ob2);
three_d operator++( );
void show();
void assign(int mx, int my, int mz);
};
// дружественная функция +
three_d operator+(three_d ob1, three_d ob2)
{
three_d temp;
temp.x = ob1.x + ob2.x;
temp.y = ob1. y + ob2.y;
8
temp.z = ob1.z + ob2.z;
return temp;
}
// перегрузка =
three_d three_d :: operator=(three_d ob2)
{
x = ob2.x;
y = ob2.y;
z = ob2.z;
return *this;
}
// перегрузка унарного ++
three_d three_d :: operator++( )
{
x++;
y++;
z++;
return *this;
}
// вывод координат x, y, z
void three_d :: show()
{
cout << x << ‘ ‘;
cout << y << ‘ ‘;
cout << z << ‘\n’;
}
// присвоение координат
void three_d assign(int mx, int my, int mz)
{
x = mx;
y = my;
z = mz;
}
int main()
{
three_d a, b, c;
a.assign(1, 2, 3);
b.assign(10, 10, 10);
a.show();
b.show();
c = a + b; // сложение а и b
c.show();
c = a + b + c; // сложение a, b и с
c.show();
9
c = b = a; // множественное присваивание
c.show();
b.show();
++c;
c.show();
return 0;
}
Во многих случаях использование функций-друзей вместо функций-элементов не
дает выигрыша при перегрузке операторов. Однако имеется одна ситуация, в которой
необходимо использовать дружественные функции. Как известно, указатель на объект,
вызывающий функцию-оператор, перегружается в указателе this. В случае бинарных операторов левый объект вызывает эту функцию-оператор. Такой способ работает до тех пор,
пока левый объект определяет заданную операцию. Предположим, что для объекта ob
определены операции присваивания и сложения. Так что следующий код
ob = ob + 10;
является корректным. Поскольку объект ob находится с левой стороны оператора +, то он
вызывает операторную функцию, перегружающую операцию сложения, которая по предположению способна добавить целое число к определенному элементу объекта ob. Однако
следующая инструкция не является корректной:
ob = 10 + ob;
Причина, по которой не будет выполняться эта инструкция не будет выполняться, заключена в том, что слева от оператора + теперь стоит целое число, являющееся встроенным
типом и не имеет функции-элемента, которая могла бы осуществить сложение с объектом
ob.
Можно использовать встроенные типы с левой стороны оператора +, если перегрузка осуществляется с помощью двух дружественных функций. В таком случае функции-оператору передаются явным образом оба аргумента и она вызывается точно также
как любая перегружаемая функция, основываясь на типе своих аргументов. Одна из версий функции-оператора + обрабатывает суммирование объект + целое, а вторая обрабатывает суммирование целое + объект. Перегрузка оператора + (или других) с использованием дружественных функций позволяет складывать переменные встроенных типов с
объектами. Следующая программа показывает реализацию такой перегрузки:
#include <iostream.h>
class CL {
public:
int count;
CL operator=(int i);
friend CL operator=(CL ob, int i);
friend CL operator=(int i, CL ob);
};
CL CL :: operator=(int i)
{
count = i;
return *this;
}
// обрабатывает ob + int
10
CL operator+(CL ob, int i)
{
CL temp;
temp.count = ob.count + i;
return temp;
}
// обрабатывает int + ob
CL operator+(int i, CL ob)
{
CL temp;
temp.count = i + ob.count ;
return temp;
}
int main( )
{
CL obj;
obj = 10;
cout << obj.count << ‘ ‘;
// выводит 10
obj = 10 + obj; // прибавление объекта к целому
cout << obj.count << ‘ ‘; // выводит 20
obj = obj+12; // прибавление целого к объекту
cout << obj.count << ‘ ‘; // выводит 32
return 0;
}
6.5 Перегрузка оператора индекса массива [ ]
Из всех перегружаемых операторов есть один достаточно экзотичный оператор,
который бывает полезно перегружать – оператор индекса массива [ ]. В С++ оператор [ ]
при перегрузке рассматривается как бинарный оператор. Его следует перегружать с помощью функции-элемента. Нельзя использовать дружественную функцию. Общая форма
функции-оператора operator[ ]() имеет следующий вид:
тип имя_класса :: operator[ ](int индекс)
{
// . . .
}
С технической точки зрения тип не обязательно должен быть целым, но поскольку
функция-оператор operator[ ](), как правило, используется для получения индекса массива, то ее параметр обычно имеет тип int.
Для заданного объекта Ob выражение
Ob[3]
Преобразуется в вызов функции operator[ ]():
operator[ ](3)
11
В таком случае значение индекса передается функции operator[ ]() в качестве явного параметра. Указатель this указывает на объект Ob, тот самый, который вызывает функцию.
В следующей программе класс atype содержит массив из трех переменных целого
типа. Конструктор инициализирует каждый элемент массива заданным значением. Перегруженная функция-оператор operator[ ]() возвращает величину элемента массива, определяемого индексом, передаваемым в качестве параметра.
#include <iostream.h>
class atype {
int a[3];
public:
atype (int i, int j, int k) { a[0] = i; a[1] = j; a[2] = k; }
int operator[ ] (int i) { return a[i];}
};
int main ()
{
atype ob(1, 2, 3);
cout << ob[1];
// выводит 2
return 0;
}
Можно создать функцию-оператор operator[ ]() таким образом, чтобы оператор [ ]
можно было использовать как с левой, так и с правой стороны оператора присваивания.
Для этого достаточно в качестве возвращаемой величины для operator[ ]() задать ссылку.
Сказанное проиллюстрировано в следующей программе:
#include <iostream.h>
class atype {
int a[3];
public:
atype (int i, int j, int k) { a[0] = i; a[1] = j; a[2] = k; }
int &operator[ ] (int i) { return a[i];}
};
int main ()
{
atype ob(1, 2, 3);
cout << ‘ ‘;
ob[1] = 25; // слева от =
cout << ob[1];
// выводит 25
return 0;
}
Поскольку operator[ ]() возвращает ссылку на элемент массива, отвечающий индексу 1, то
он может быть использован с левой стороны оператора присваивания для модификации
элемента массива. Разумеется, этот оператор может быть использован и с правой стороны
оператора присваивания.
Как известно, в С++ во время выполнения программы можно выйти за пределы
массива, и при этом не будет выдаваться сообщение об ошибке. Поэтому одним из достоинств перегрузки оператора [ ] служит то, что с его помощью можно предотвратить подобные эффекты.
12
Если создается класс, содержащий массив, и разрешен доступ к этому массиву
только через перегруженный оператор [ ], то можно перехватывать значение, содержащее
величину индекса за пределами допустимых значений. Например, следующая программа
осуществляет проверку на принадлежность значений индекса допустимой области:
#include <iostream.h>
#include <stdlib.h>
class atype {
int a[3];
public:
atype (int i, int j, int k) { a[0] = i; a[1] = j; a[2] = k; }
int &operator[ ] (int i) ;
};
// проверка диапазона для atype
int &atype :: operator[ ] (int i)
{
if (i < 0 || i > 2) {
cout << “Выход за пределы индекса\n”;
exit(1);
}
return a[i];
}
int main ()
{
atype ob(1, 2, 3);
cout << ‘ ‘;
ob[1] = 25; // слева от =
cout << ob[1];
// выводит 25
ob[3] = 44; // генерируется ошибка выполнения
return 0;
}+
При выполнении инструкции
ob[3] = 44;
Функция-оператор перехватывает ошибку выхода за пределы допустимой области и программа заканчивает свою работу.
7 Варианты заданий
Вариант 1
Определить класс Vec4 как вектор из четырех float. Определите оператор
operator[] для Vec4. Определите операторы +, , *, /, =, +=, =, *= и /= для комбинаций
векторов и чисел с плавающей точкой.
Написать программу, демонстрирующую работу с этим классом.
Вариант 2
13
Используя перегрузку функций и перегрузку операторов, создайте класс String.
Строку представить как массив символов с размещением в динамической памяти. Изучите
функции обработки строк библиотеки языка С и реализуйте каждую из этих функций как
элементы класса String путем перегрузки операторов. Используйте затем эти функции
для выполнения операций с текстами.
Вариант 3
Определите класс RINT, который ведет себя как int, за исключением того, что
допустимы только операторы +,  (унарный и бинарный), *, /, %. Подсказка: не
определяйте RINT :: operator int ().
Написать программу, демонстрирующую работу с этим классом.
Вариант 4
Определите класс Matr4 как вектор состоящий из четырех Vec4 (cм. вариант 1).
Определите operator[ ](), возвращающий Vec4 из Matr4. Определите операции
операции транспонирования матрицы, умножения матрицы на число, сложения матриц и
умножения матриц.
Написать программу, демонстрирующую работу с этим классом.
Вариант 5
Создать класс Point для работы с точками на плоскости. Координаты точки 
декартовы. Обязательно должны быть реализованы: перемещение точки по оси X,
перемещение по оси Y, определение расстояния до начала координат, расстояния между
двумя точками, преобразование в полярные координаты, сравнение на совпадение и
несовпадение.
Написать программу, демонстрирующую работу с этим классом.
Вариант 6
Создать класс Time для работы со временем в формате «час:минута:секунда».
Класс должен включать в себя не менее четырех функций инициализации: числами, строкой
(например, «23:59:59»), секундами и временем. Обязательными операциями являются:
вычисление разницы между двумя моментами времени в секундах, сложение времени и
заданного количества секунд, вычитание из времени заданного количества секунд,
сравнение моментов времени, перевод в секунды, перевод в минуты (с округлением до
целой минуты).
Написать программу, демонстрирующую работу с этим классом.
Вариант 7
Описать класс Matrix, реализующий тип данных «вещественная матрица» и работу с ними. Класс должен реализовывать путем перегрузки операторов следующие операции над матрицами:
 сложение, вычитание, умножение, деление (+, -, *, /) (умножение и деление, как
на другую матрицу, так и на число);
 комбинированные операции присваивания (+=, -=, *=, /=);
 операции сравнения на равенство/неравенство;
 операции вычисления обратной и транспонированной матрицы, операцию возведения в степень;
 методы вычисления детерминанта и нормы;
 методы, реализующие проверку типа матрицы (квадратная, диагональная, ну-
14
левая, единичная, симметрическая, верхняя треугольная, нижняя треугольная);
 операции ввода/вывода в стандартные потоки.
Написать программу, демонстрирующую работу с этим классом.
Вариант 8
Разработайте класс Set (множество). Внутренним представлением класса Set является массив элементов множества (массив располагается в динамической памяти). Класс
должен обеспечивать следующие возможности:
 добавление элементов в множество с исключением дублирования элементов;
 удаление элементов из множества;
 вывод множества на экран;
 выполнение операций пересечения, объединения и разности множеств;
 выполнение операций сравнения множеств.
Написать программу, демонстрирующую работу с этим классом.
Вариант 9
Разработайте класс Complex для представления комплексных чисел. Этот класс
позволяет осуществлять операции над комплексными числами. Они представляются в виде Re + Im*i, где i имеет значение корня квадратного из 1. С помощью этого класса
должны выполняться следующие операции над комплексными числами:
 ввод и вывод комплексного числа посредством перегрузки операций >> и <<;
 присваивание значения одного комплексного числа другому путем перегрузки
операции =;
 сложение, вычитание и умножение комплексных чисел путем перегрузки соответственно операций +,  и *;
 сравнение двух комплексных чисел путем перегрузки операций <, >, = =.
Написать программу, демонстрирующую работу с этим классом.
Вариант 10
Разработайте класс Polinomial (полином). Внутренним представлением класса
Polinomial является массив членов полинома (массив располагается в динамической
памяти). Каждый член содержит коэффициент и показатель степени. Член 2х4 имеет коэффициент 2 и показатель степени 4. Разработайте полный класс, содержащий соответствующие функции конструктора, конструктора копий, деструктора, а также функцию
инициализации set() и функцию вывода на экран get(). Класс должен обеспечивать путем использования перегруженных операций следующие возможности:
 перегрузить операцию сложения (+), чтобы складывать два объекта класса Polinomial;
 перегрузить операцию вычитания (), чтобы вычитать два объекта класса Polinomial;
 перегрузить операцию присваивания (=), чтобы присваивать один объект класса
Polinomial другому;
15
 перегрузить операцию умножения, чтобы перемножать два объекта класса Polinomial;
 перегрузить операции сложения с присваиванием (+=), вычитания с присваиванием (=), операцию умножения с присваиванием (*=).
Написать программу, демонстрирующую работу с этим классом.
Вариант 11
Создать класс StringType для эффективной работы со строками, позволяющий
форматировать и сравнивать строки, хранить в строках числовые значения и извлекать их.
Строки должны быть представлены в виде массива символов с размещением в динамической памяти. Для данного класса необходимо реализовать:
 перегруженные операции присваивания и конкатенации;
 операции сравнения и приведения типов;
 преобразование в число любого типа;
 форматный вывод строки.
Написать программу, демонстрирующую работу с этим классом.
Вариант 12
Создайте класс RationalNumber (дроби) со следующими возможностями:
 создайте конструктор, который предотвращает равенство нулю знаменателя
дроби, сокращает или упрощает дроби, если они не в сокращенной форме, и исключает отрицательные знаменатели;
 перегрузите операции сложения, вычитания, умножения и деления для этого
класса;
 перегрузите операции отношения и проверки на равенство для этого класса.
Написать программу, демонстрирующую работу с этим классом.
Вариант 13
Реализовать класс FazzyNumber для работы с нечеткими числами, которые представляются тройками чисел (х–е1,х,х+e2). Для чисел А = (Ааl,А,А+аr) и В =
(Bbl,В,В+br) арифметические операции выполняются по следующим формулам:





сложение А + В = (А+Вalbl,А+В,А+В+ar+br);
вычитание А  В = (А  В  al  bl,А В,А  В + аr+ br);
умножение А * В = (А*В  В*al  А*bl + al*bl, A*В, A*B + B*ar +A*br + ar* br);
обратное число А = (1 / (A + аr),1 /А,1/(А  al)), А > 0;
деление А / В = ((А  al) / (В + Br), А / В, (А + ar) / (В  bl)), В > 0.
Написать программу, демонстрирующую работу с этим классом.
Вариант 14
Комплексное число представляются парой действительных чисел (а,b), где а 
действительная часть, b  мнимая часть. Реализовать класс Complex для работы с комплексными числами. Обязательно должны присутствовать операции:
 сложения add, (а,b) + (с,d) = (а+c,b+d);
16
 вычитания sub, (а,b)  (с,d) = (аc,bd);
 умножения mul, (а,b) * (с,d) = (ас  bd, ad + bc);
 деления div, (а,b) / (с,d) = (ас + bd, bc  ad) / (с2 + d2);
 сравнение equ, (a,b) = (с,d), если (а = с) и (b = d);
 сопряженное число conj, conj(a,b) = (а,b).
Написать программу, демонстрирующую работу с этим классом.
Вариант 15
Рациональная (несократимая) дробь представляется парой целых чисел (а,b), где а 
числитель, b  знаменатель. Создать класс Rational для работы с рациональными дробями. Обязательно должны быть реализованы операции:

сложения add, (а,b) + (с,d) = (ad + bc,bd);

вычитания sub, (a,b)  (с,d) = (ad  bc,bd);

умножения mul, (a,b) *(c,d) = (ac,bd);

деления div, (a,b) / (с,d) = (ad,bc);

сравнения equal, greate, less.
Должна быть реализована приватная функция сокращения дроби reduce(), которая обязательно вызывается при выполнении арифметических операций.
Написать программу, демонстрирующую работу с этим классом.
Вариант 16
Номиналы российских рублей могут принимать значения 1, 2, 5, 10, 50, 100, 500,
1000, 5000. Копейки представить как 0.01 (1 копейка), 0.05 (5 копеек), 0.1 (10 копеек),
0.5 (50 копеек). Создать класс Money для работы с денежными суммами. Сумма должна
быть представлена полями-номиналами, значениями которых должно быть количество купюр данного достоинства. Реализовать сложение сумм, вычитание сумм, деление сумм, деление суммы на дробное число, умножение на дробное число и операции сравнения. Дробная часть (копейки) при выводе на экран должны быть отделена от целой части запятой.
Написать программу, демонстрирующую работу с этим классом.
Download