Перегрузка операций для класса
Допустим, мы сделали классы для точки и треугольника. И далее хотим вводить и выводить их легко через 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; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!