Перегрузка операций для класса



Допустим, мы сделали классы для точки и треугольника. И далее хотим вводить и выводить их легко через cin и cout. Например: point M; cin >> M; О том как это сделать и будет данная глава. Ну и также как сделать арифметические операции: сложение 2 точек, вычитание и прочее. Чтобы мы могли писать просто:

point M,N; cin >> M >> N; cout << M + N << endl;

Запоминай конструкцию:

 

// перегрузка оператора вывода для точки

ostream& operator<<(ostream& output, point& M) {

           cout << "("<< M.x << ", " << M.y << ", " << M.z << ")";

           return output;

}

 

// перегрузка оператора ввода для точки

istream& operator>>(istream& input, point& M) {

           cin >> M.x >> M.y >> M.z ;

           return input;

}

Вместо input может быть любое другое название переменной, в остальном же ничего менять не стоит. Для другого класса вместо point& M, нужно писать сам другой класс: например для треугольника: tring& M. Ну и само имя M тоже не принципиально.

Т.е. если я буду вводить, например, cin >> M; то у меня будет выполняется то что я прописал в функции istream operator: cin >> M.x >> M.y >> M.z ; И далее возвращается объект типа istream, а именно поток ввода. Для вывода (ostream) всё аналогично.

Например, мы создали: point M(1,2,4); и далее пишем cout << M; у нас выведется (1,2,4)- это то что мы прописали в функции ostream operator. Можешь сам поэкспериментировать.

Вот перегрузка операторов ввода-вывода для треугольника:

ostream& operator<<(ostream& output, tring& M) {

           cout << "A" << M.A << ", B" << M.B << ", C" << M.C << endl;

           return output;

}

istream& operator>>(istream& input, tring& M) {

           cout << "А:\n"; cin >> M.A;

           cout << "В:\n"; cin >> M.B;

           cout << "С:\n"; cin >> M.C;

           return input;

}

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

point operator+(point a, point b) {

           point c;

           c.x= a.x + b.x;

           c.y= a.y + b.y;

           c.z= a.z + b.z;

           return c;

}

point operator-(point a, point b) { point c; c.x= a.x - b.x; c.y= a.y - b.y; c.z= a.z - b.z; return c; }

 

// скалярное произведение

double operator%(point a, point b) { return a.x*b.x + a.y*b.y + a.z*b.z; }

 

// векторное произведение

point operator*(point a, point b) {

           point c;

           c.x= a.y*b.z - a.z*b.y;

           c.y= a.z*b.x - a.x*b.z;

           c.z= a.x*b.y + a.y*b.x;

           return c;

}

 

// проверка равенства 2 точек

bool operator==(point a, point b) {

           if ( (a.x==b.x)&&(a.y==b.y)&(a.z==b.z) ) return true;

           else return false;

}

Например, я пишу: point M,N; cin >> M >> N; И далее я могу делать с этими точками операции, которые прописал в перегрузках, например: double s= M%N; тут я вычислил скалярное произведение 2 точек. Или так: point P= M*N; тут будет уже векторное произведение.

Или могу даже использовать условия: if (M==N) { что-то делать } т.к. проверка равенства прописана в перегрузке.

Перегрузку можно прописать и при самом объявлении класса. Например, ту же проверку равенства 2 точек можно записать внутри класса так:

bool operator==(point b) {

           if ( (x==b.x)&&(y==b.y)&(z==b.z) ) return true;

           else return false;

}

 

Прототипы

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

Например, возьмём класс двумерной точки:

class point {

public:

           double x,y; point (double x1=0, double y1=0) { x=x1; y=y1; }

           double module() { return sqrt(x*x+y*y); }

};

Можно эту функцию модуля вынести за класс таким образом- в самом классе оставить только прототип, а саму функцию прописать снаружи с определённой пометкой:

class point {

public:

           double x,y; point (double x1=0, double y1=0) { x=x1; y=y1; }

           double module();

};

double point::module() { return sqrt(x*x+y*y); }

Вот и всё. Можно также вынести и функцию конструктора класса. Тогда всё будет выглядеть так:

class point {

public:

           double x,y;

           point (double,double);

           double module();

};

double point::module() { return sqrt(x*x+y*y); }

point::point(double x1=0, double y1=0) { x=x1; y=y1; }

Это нужно для того чтобы мы могли выносить все эти функции в отдельный файл. В заголовочном файле, который назовём, скажем, point.h, мы можем оставлять только прототипы- они будут показывать что класс умеет- какие у него методы. Чисто демонстрация функционала без ничего лишнего. А сама реализация этих методов, этого функционала будет в отдельном файле.

Тогда в point.h у нас будет лежать класс с прототипами:

class point {

public:

           double x,y;

           point (double,double);

           double module();

};

И для реализаций мы должны создать файл cpp, но назвать его как заголовочный, т.е. point.cpp. В этом 3-м файле будет уже исполнение:

double point::module() { return sqrt(x*x+y*y); }

point::point(double x1=0, double y1=0) { x=x1; y=y1; }

Ну и естественно, в файле cpp мы должны подключить сам файл h, т.е. #include "point.h" в файл point.cpp тоже нужно написать.

Тогда вся программа будет выглядеть так:

// Файл main.cpp

#include "point.h"

int main() { point a(2,4); cout << a.module() << endl; system("pause"); }

 

// Файл point.h

#include <iostream>

#include <cmath>

using namespace std;

class point { public: double x,y; point (double,double); double module(); };

 

// Файл point.cpp

#include "point.h"

double point::module() { return sqrt(x*x+y*y); }

point::point(double x1=0, double y1=0) { x=x1; y=y1; }

 

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

Например, добавим в файл main.cpp какую-нибудь функцию, пусть будет вычисление квадрата числа. Можно сделать и таким образом:

#include "point.h"

double q(double x);

int main() { point a(2,4); cout << a.module() << endl; system("pause"); }

double q(double x) { return x*x;}

 

Шаблоны классов

Так же как и в случае с функциями, для классов нам тоже могут понадобиться шаблоны. Например, возьмём тот же класс точки, для удобства поместим всё в 1 файл main.cpp. Мы прописали что координаты у нас могут иметь тип double. А если я хочу, например, чтобы координаты были только целые, т.е. типа int, тогда нужно прописывать отдельный класс. А есть же ещё типы float, long long int и прочие- и для каждого придётся создавать новый отдельный класс. Чтобы этого избежать, мы вводим шаблон для класса:

#include <iostream>

#include <cmath>

using namespace std;

// класс двумерной точки с шаблоном

template <typename Type>

class point {

public:

           Type x,y;

           point (Type x1=0, Type y1=0) { x=x1; y=y1; }

           double module() { return sqrt(x*x+y*y); }

};

// главная функция

int main() {

           setlocale(0,"");

           point <int>a(2,4);

           cout << a.module() << endl;

           system("pause");

}

Важный момент: при создании точки нужно указывать уже тип координат, как мы это сделали тут: point <int>a(2,4); Иначе не будет компилироваться и вообще работать.

...

Шаблонов может быть несколько. Например, мы хотим чтобы координаты у нас могли быть разного типа. Тогда сделаем 2 шаблона для каждой координаты:

#include <iostream>

#include <cmath>

using namespace std;

template <typename M, typename N>

class point {

public:

           M x; N y; point (M x1=0, N y1=0) { x=x1; y=y1; }

           double module() { return sqrt(x*x+y*y); }

};

int main() {

           setlocale(0,"");

           point <int,double>a(2,4);

           cout << a.module() << endl;

           system("pause");

}

В более старых программах вместо ключевого слова typename можно также встретить class, но можно использовать и typename- это универсальное слово для функций и классов.

...

Вместо типов typename можно указывать обычные в шаблоне, например:

template <typename M, int n>

И далее в классе это может как-то использоваться. Например, от числа n у нас будет зависеть модуль:

template <typename M, int n>

class point {

public:

           M x; M y;

           point (M x1=0, M y1=0) { x=x1; y=y1; }

           double module() {

                           if(n==1) return sqrt(x*x+y*y);

                           if(n==2) return 0;

           }

};

И далее мы можем вызывать это так:

point <float,2> a(2,4);

cout << a.module() << endl;

Можно задать значение по умолчанию:

template <typename M, int n=1>

И если мы напишем просто point <float> a(2,4); то у нас будет n=1 по умолчанию как мы указали. Если хотим сделать n=2, то надо написать всё же эту метку: point <float,2> a(2,4);

...

По умолчанию могут быть и сами типы, например: template <typename M= int, int n=1>

Только важный момент: точку объявлять нужно так: point<> a(2,4); Оставляем пустые угловые скобки.

 

Наследование классов

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

Посмотрим опять на геометрических абстракциях, просто тут легко приводить примеры. Сделаем новый тип данных- шар. Т.е. класс шара. Шар у нас зависит от точки центра и радиуса. Также у шара будет площадь поверхности и объём. Естественно, у нас уже будет класс для трёхмерной точки. Этот класс можно сделать даже с шаблоном как мы уже научились. Но для простоты сделаем пока просто типа double.

const double pi= 3.141592653;

class point {

public: double x,y,z; point (double x1=0, double y1=0, double z1=0) { x=x1; y=y1; z=z1; }

};

// сам класс для шара теперь

class sphere {

public:

           point center; double radius;

           sphere (point c1=(0,0,0), double r1=0) { center=c1; radius=r1; }

           double square() { return 4*pi*radius*radius; }

           double volume() { return 4*pi*radius*radius*radius/3; }

};

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

class Msphere {

public:

           point center; double radius; double density;

           Msphere (point c1=(0,0,0), double r1=0, double d1=1) { center=c1; radius=r1; density=d1; }

           double square()          { return 4*pi*radius*radius; }

           double volume() { return 4*pi*radius*radius*radius/3; }

           double mass() { return density*volume(); }

};

А если мы теперь хотим чтобы у нас был и заряженный шар- т.е. у него добавится ещё поверхностная плотность заряда в качестве атрибута и заряд в качестве метода. Нужен новый класс и всё копировать с предыдущего. Возникает много лишней писанины, например методы площади и объёма будут во всех классах. Нельзя ли один раз это где-то написать, а дальше использовать? Да, можно- благодаря наследованию.

Возьмём общий шар без массы и зарядов как родительский класс. И от него пронаследуем шар с массой. Сам родительский класс останется без изменений, а дочерний (шар с массой) будет выглядеть так:

class Msphere : public sphere {

public:

           double density;

           Msphere (point c1=(0,0,0), double r1=0, double d1=1) { center=c1; radius=r1; density=d1; }

           double mass() { return density*volume(); }

};

Т.е. мы уже убрали отсюда атрибуты центра и радиуса и методы площади и объёма, т.к. дочерний класс умеет также всё то что и родительский. Наследование делается, как можешь увидеть, через : public sphere. Конструктор тут новый, поэтому его мы не отнаследовали.

Заряженный шар можно отнаследовать уже от массивного:

class Qsphere : public Msphere {

public:

           double charge_density;

           Qsphere (point c1=(0,0,0), double r1=0, double d1=1, double q1=1) { center=c1; radius=r1; density=d1; charge_density=q1;}

           double charge() { return charge_density*volume(); }

};

И далее можно проверить- создать заряженный шар и посчитать всё:

point a; Qsphere f(a,2); // тут плотности будут по умолчанию равны 1, как указано в конструкторе класса, центр по умолчанию в точке (0,0,0)

cout << f.square() << endl;

cout << f.volume() << endl;

cout << f.mass() << endl;

cout << f.charge() << endl;

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

class bomb {

public:

           int boom_time; double boom_radius;

           bomb (int a=100, double b=1) { boom_time=a; boom_radius=b; }

};

И отнаследуем заряженный шар также от класса бомбы:

class Qsphere : public Msphere, public bomb {

public:

           double charge_density;

           Qsphere (point c1=(0,0,0), double r1=0, double d1=1, double q1=1) { center=c1; radius=r1; density=d1; charge_density=q1;}

           double charge() { return charge_density*volume(); }

};

Т.е. теперь наш заряженный шар может играть также роль бомбы. Единственное, мы в конструкторе не дописали переменные бомбы- это значит что заряженный шар принимает эти значения по умолчанию, которые указаны в конструкторе класса бомбы. Мы сами можем занести эти значения и без конструктора, кстати. Например, так:

point a; Qsphere f(a,2); cin >> f.boom_time;

cout << f.boom_time << endl;

Мы можем создавать также несколько конструкторов класса- например, добавить в заряженный шар конструктор, где не надо указывать центр:

Qsphere (double r1=0, double d1=1, double q1=1) { radius=r1; density=d1; charge_density=q1;}

И тогда можем создавать так: Qsphere f(2); Это значит, что у нас тут радиус равен 2, а плотности (2 следующих аргумента конструктора) остаётся по умолчанию равные 1. Это уже знакомая нам перегрузка функций. Компилятор сам определяет, какой конструктор, по перечисленным аргументам: point, double, double, double или же просто double, double, double. Только просто так создавать Qsphere s; и что-то с этим делать нельзя- компилятор не поймёт, какой из конструкторов использовать и работать не будет. Нужно либо иметь в классе 1 конструктор, либо указывать в скобках при создании нового заряженного шара хоть какие-то аргументы конструктора, по типам которых можно будет определить какой именно конструктор использовать.

...

Ну и тут стоит ещё упомянуть- мы наследовали обычно через public. class Msphere : public sphere. Но можно наследовать и через private, т.е. написать class Msphere : private sphere. Тогда в дочернем классе все наследуемые вещи станут в категории private.

Также помимо public и private есть тип protected, который используется реже. Мы можем наследовать через него. Это значит что в дочернем классе будут доступны атрибуты и методы родительского класса, но вне этих классов не будут. Также в самом классе мы можем помечать какие-то атрибуты и методы в протектед (protected: и дальше эти атрибуты и методы пишем), тогда они будут доступны только в дочерних классах и самом родительском классе, а в остальной программе (снаружи всей этой цепочки классов) не будут. Наследуемые вещи переходят в дочернем классе в категорию protected.

 

Полиморфизм

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

void info() { cout << "Обычный шар" << endl; }

И аналогично сделаем для 2 других шаров. Уберём использование центральной точки и бомбы чтобы было проще. Получим:

class sphere {

public:

           double radius;

           sphere ( double r1=0) { radius=r1; }

           double square()          { return 4*pi*radius*radius; }

           double volume() { return 4*pi*radius*radius*radius/3; }

           void info() { cout << "Обычный шар" << endl; }

};

class Msphere : public sphere {

public:

           double density;

           Msphere (double r1=0, double d1=1) { radius=r1; density=d1; }

           double mass() { return density*volume(); }

           void info() { cout << "Массивный шар" << endl; }

};

class Qsphere : public Msphere{

public:

           double charge_density;

           Qsphere (double r1=0, double d1=1, double q1=1) { radius=r1; density=d1; charge_density=q1;}

           double charge() { return charge_density*volume(); }

           void info() { cout << "Заряженный шар" << endl; }

};

Для каждого шара есть свой метод инфо, который выводит разное. Мы можем это протестировать:

sphere s1; Msphere s2; Qsphere s3;

s1.info(); s2.info(); s3.info();

Заметим, что метод у нас называется одинаково, при этом объекты однотипные. Возникает вопрос- можно ли как-то это объединить и использовать info в цикле в массиве? Т.е. в 1 массив объединить обычный шар, массивный и заряженный, а далее их использовать как нужно. Да, это можно сделать с помощью полиморфизма. Чтобы проще было понять, в данном случае полиморфизм- возможность объединить в одном массиве элементы разного типа.

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

sphere *s1= new sphere; s1->info();

Когда мы работаем с указателями, вместо точки при вызове метода используется стрелочка. А именно, это 2 символа подряд, которые ты можешь видеть.

Суть в том, что мы в родительском классе можем ссылаться на дочерний, т.е. написать так: sphere *s1= new Qsphere; s1->info(); И выведется инфо для обычного шара. Всё осталось так же, поменялось только вместо new sphere стало new Qsphere.

Если мы хотим выводить метод инфо для заряженного шара, используя обычный и ссылки, то при объявлении обычного класса нужно сделать функцию инфо виртуальной, а именно написать вначале ключевое слово virtual. Это будет выглядеть так:

class sphere {

public:

           double radius;

           sphere ( double r1=0) { radius=r1; }

           double square()          { return 4*pi*radius*radius; }

           double volume() { return 4*pi*radius*radius*radius/3; }

           virtual void info() { cout << "Обычный шар" << endl; }

};

Тогда при таком коде: sphere *s1= new Qsphere; s1->info();  выведется уже инфо для Qsphere. Притом что если мы и массивный шар так объявим: Msphere *s2= new Qsphere; s2->info(); то всё равно выведется заряженный шар несмотря на то что в самом Msphere функция info не виртуальная. Т.е. виртуальность, если так можно выразиться, в данном случае тоже наследуется.

Если функция виртуальная, то дочерние классы могут её как бы переписывать по-своему. И через указатели мы уже можем это использовать.

Далее это мы уже применим всё для полиморфизма:

sphere *m[3];

m[0]= new sphere;

m[1]= new Msphere;

m[2]= new Qsphere;

for(int i=0; i<3; i++) m[i]->info();

При этом нужно сделать виртуальной функцию инфо в нашем родительском классе sphere. И тогда выведется:

Обычный шар

Массивный шар

Заряженный шар

Т.е. как видишь благодаря полиморфизму мы можем объединить в массив объекты разного типа и по одному имени метода делать разные вещи. Вот и вся суть.

Когда одна и та же переменная может быть разных типов, она называется полиморфной. В данном случае *m является полиморфной переменной. Также аналогично есть полиморфные функции- это мы уже видели, в частности, в шаблонах.

Если в языке программирования за переменной чётко фиксирован её тип, то говорят что в языке статитическая типизация данных. Например, мы пишем int a= 6; и всё- переменная a имеет целый тип и никакой другой. В с++ у нас статическая типизация, как и например в языках си, паскаль, java. Задачу полиморфизма в языке с++ мы решаем через наследование классов, виртуальных функций и указателей- т.е. всё достаточно сложно. Но тем не менее такая "полиморфная" возможность есть.

А бывают языки с динамической типизацией, например python или ruby. При динамической типизации не нужно указывать тип переменной- он автоматически определяется. Можно просто написать: a= 6; и всё, компилятор сам поймёт что это целое число. Потом можно изменить значение переменной a на дробное, логическое, строку и т.п. Все переменные при динамической типизации полиморфные и здесь уже всё само по себе может объединяться без дополнительных ухищрений.

Также можно сделать в родительском классе функцию чисто виртуальной, т.е. написать так: virtual void info() = 0; И тогда все дочерние классы обязаны теперь иметь эту функцию info, иначе работать не будет. Т.е. мы можем создавать абстрактный родительский класс из этих виртуальных нулевых функций как источник, каркас- а далее от него наследовать похожие классы, которые обязаны иметь функции, которые в родительском объявлены как виртуальные и нулевые.

И для полиморфизма также есть такая формулировка: единый интерфейс- множество реализаций. Интерфейсом называют всю совокупность методов класса, которые находятся в категории public.

 

Дружественные функции

Дружественные функции имеют доступ к закрытым и защищённым полям класса. К примеру, создадим класс точки и пусть у неё будет скрыта координата z.

class point {

           double z;

public:

           double x,y;

           point () {

                           x=0; y=0; z=0;

           }

};

Пусть у нас снаружи будет функция, которая выводит точку:

void dim3print(point c) {

           cout << "(" << c.x << "," << c.y << "," <<c.z << ")" << endl;

}

Но работать эта функция не будет, т.к. она использует скрытую координату z- её можно использовать только внутри класса. Чтобы всё-таки это работало, нужно внутри класса объявить эту функцию в виде прототипа как дружескую:

friend void dim3print(point);

Тогда всё будет работать. Вот пример полной программы:

#include <iostream>

#include <cmath>

using namespace std;

class point {

           double z;

public:

           double x,y;

           point () { x=0; y=0; z=0; }

           friend void dim3print(point);

};

void dim3print(point c) {

           cout << "(" << c.x << "," << c.y << "," <<c.z << ")" << endl;

}

int main() {

           setlocale(0,"");

           point x; x.x=2; x.y=3; dim3print(x);

           system("pause");

}

 

Пример работы с классом

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

#ifndef KATMUR_VAKIT

#define KATMUR_VAKIT

 

// библиотеки ввода и вывода

#include <iostream>

using namespace std;

 

// другие библиотеки, которые могут пригодиться

#include <cmath>

#include <string>

 

// функции, которые могут пригодиться

int CharToInt (char c) { return (int)c - (int)'0'; }

int StringSize (string s) {

           char c= s[0]; int i=0; int stringsize=0;

           while ((int)c != 0) { i++; c=s[i]; stringsize++; }

           return stringsize;

}

int StringToInt (string s) {

           int sum=0;          

           for (int I=0; I<StringSize(s); I++) sum+=CharToInt(s[I])*pow(10,StringSize(s)-I-1);

           return sum;

}

bool isDigit (char c) {

           if(((int)c>=48)&&((int)c<=57)) return true;

           else return false;

}

bool isNumber (string s) {

           bool *Digit = new bool [StringSize(s)];

           for(int I=0;I<StringSize(s);I++) Digit[I]=false;

           for(int I=0;I<StringSize(s);I++) {

                           if(isDigit(s[I])) Digit[I]=true;

           }

           bool Rez=true;

           for(int I=0;I<StringSize(s);I++) Rez*=Digit[I];

           return Rez;

}

///////////////////////////////////////////////////////////

 

// сам класс времени

class vakit {

           bool neg; // отрицательное ли время- в блоке private

           // перевод из кол-ва минут в сумме в формат времени (76 -> 1:16 напр)

           vakit AbsoluteToVakit (int x) {

                           vakit res;

                           res.hours= x/60; res.minutes= x%60;

                           return res;

           }

public:

           int hours, minutes; // часы и минуты

           vakit(int h=0, int m=0) { hours=h; minutes=m; neg= false; } // конструктор

           //

           // абсолютное количество минут

           int absolute() {

                           if(!neg) return 60*hours + minutes;

                           if(neg) return 60*hours - minutes;

           }

           // количество целых суток

           int teulek() { return absolute()/1440; }

           //

           // перегрузки арифметических действий

           vakit operator+(vakit x) {

                           vakit res;

                           res.hours= hours + x.hours;

                           res.minutes= minutes + x.minutes;

                           if(res.minutes>=60) { res.hours++; res.minutes-=60; }

                           return res;

           }

           vakit operator-(vakit x) {

                           vakit res;

                           if(absolute() < x.absolute()) {

                                      res.hours= x.hours - hours;

                                           res.minutes= x.minutes - minutes;

                                           if(res.minutes<=0) { res.hours--; res.minutes+=60; }

                                           res.hours = -res.hours;

                                           res.neg= true;

                           }

                           else {

                                           res.hours= hours - x.hours;

                                           res.minutes= minutes - x.minutes;

                                           if(res.minutes<=0) { res.hours--; res.minutes+=60; }

                           }

                           return res;

           }

           vakit operator*(int n) {

                           vakit res;

                           res.hours= hours*n;

                           res.minutes= minutes*n;

                           if(res.minutes>=60) { res.hours+= (res.minutes/60); res.minutes-= (60*(res.minutes/60)); }

                           return res;

           }

           vakit operator/(int n) {

                           vakit res;

                           res.hours= hours/n;

                           res.minutes= ( minutes + (hours%n)*60 ) / n;

                           return res;

           }

           //

           // перегрузки логических сравнений

           bool operator==(vakit x) {

                           if (absolute() == x.absolute()) return true;

                      else return false;

           }

           bool operator<(vakit x) {

                           if (absolute() < x.absolute()) return true;

                           else return false;

           }

           bool operator>(vakit x) {

                           if (absolute() > x.absolute()) return true;

                           else return false;

           }

           bool operator<=(vakit x) {

                           if (absolute() <= x.absolute()) return true;

                           else return false;

           }

           bool operator>=(vakit x) {

                           if (absolute() >= x.absolute()) return true;

                           else return false;

           }

           //

           vakit operator%(int n) {

                           return AbsoluteToVakit(absolute() - 1440*n);

           }

           vakit pure() {

                      return AbsoluteToVakit(absolute() - 1440*teulek());

           }

           //

           // делаем friend чтобы функция вывода считывала также private поля

           friend ostream& operator<<(ostream& , vakit&);

};

 

// перегрузки вывода и ввода (потоков)

ostream& operator<<(ostream& str, vakit& x) {

           if(x.neg) cout << "-";

           cout << abs(x.hours) << ":";

           if( (x.minutes>=0) && (x.minutes<=9) ) cout << "0";

           cout << x.minutes;

           return str;

}

istream& operator>>(istream& str, vakit& x) {

           string s; cin>>s;

           bool pass= true;

           //

           bool point= false;

           for (int i=0; i<StringSize(s); i++) {

                           if(s[i]==':') {

                                           point=true;

                                           break;

                           }

           }

           pass*= point;

           //

           if(pass) {

                           string hour="", mins= "";

                           int i=0;

                           while(s[i]!=':') hour+=s[i++];

                           i++;

                           while(s[i]!='\0') mins+=s[i++];

                           pass*= (isNumber(hour) && isNumber(mins));

                           if(pass) {

                                           x.hours= StringToInt(hour);

                                           x.minutes= StringToInt(mins);

                           }

           }

           if(!pass) cout << "Input Error !!!" << endl;

           return str;

}

 

// дополнительные функции и заранее заданные типа константы

vakit mid(12,0);

vakit realmid (vakit a, vakit b) { return (a+b)/2; }

vakit fazla (vakit a, vakit b) { return realmid(a,b) - mid; }

 

// конец заголовочного файла

#endif

 

________________________________________________

РАЗДЕЛ 3


Дата добавления: 2018-09-22; просмотров: 278; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!