Класс рациональных чисел на C++

от автора

Предисловие

В данной статье будет рассмотрен пример простейшего класса на C++ — класса рациональных чисел. Будут приведены примеры перегрузки унарных и бинарных операций как членов класса, а также перегрузка операторов потоков istream и ostream, как дружественных (friend) функций.
Статья поможет начинающим программистам разобраться в классах, в перегрузке и в некоторых особенностях языка.

Все примеры протестированы в среде NetBeans 7.3 (Ubuntu 12.10).

Начало

Для начала создадим 3 файла в среде разработки для разделения кода на части.

  • main.cpp — тело программы
  • realize.cpp — реализация методов класса
  • rational.h — описание класса

rational.h

Далее приведено описание класса рациональных чисел. Назовем его rat.

#ifndef RATIONAL_H  #define	RATIONAL_H  // для корректного подключения заголовка  class rat {  int ch; // числитель int zn; // знаменатель friend istream &operator>>(istream&, rat&); // ввод friend ostream &operator<<(ostream&, rat&); //вывод int nod(int, int);  // наибольший общий делитель int abs(int); // модуль числа public: rat(int=0, int=1); // конструктор void norm(); // функция для нормализации числа  rat operator*(const rat&) const; // умножение рациональных (дробных) чисел rat operator+(const rat&) const; // прибавление  рациональных (дробных) чисел rat operator-(const rat&) const; // вычитание рациональных (дробных) чисел rat operator/(const rat&) const; // деление рациональных (дробных) чисел  }; #endif 

Переменные ch и zn соответственно числитель и знаменатель рационального числа. Функции nod() и abs() объявление в private, чтобы ограничить доступ вне класса, ибо они нам снаружи не нужны.

Обратите внимание, что потоки istream (>>) и ostream (<<) перегружены как дружественные функции, а арифметические операторы — как члены класса.
На самом деле мы могли перегрузить операции +,-,*,/ как дружественные функции, но не >> и <<.

Дело в том, что если перегрузить их как членов класса, тогда мы будем иметь один параметр в функции типа rat, но нам нужно, чтобы было два параметра, первый из которых будет иметь тип istream или ostream.

main.cpp

Рассмотрим тело программы перед тем, как напишем реализации методов класса rat.

#include <iostream>  #include "rational.h" using namespace std; int main() { rat a, b; //  объявление объектов типа rat   cin >>a;  // ввод первого рационального числа cin >> b; // ввод второго рационального числа cout << a+b << endl; // сумма двух рациональных чисел return 0; } 

Как вы наверно заметили, тело программы максимально упрощено. Мы не стали вызывать методы класса, вместо этого, мы использовали стандартные операторы для работы с объектами. Это одна из преимуществ ООП.

realize.cpp

Далее опишем функции, которые перегружают операторы.

Исходный код файла realize.cpp

 #include <iostream> #include "rational.h" using namespace std; int rat::abs(int x) { if(x<0) return (-1)*x; return x; } int rat::nod(int x, int y) { if(x==0 || y==0) return 1; while(x!=y) if(x>y) x=x-y; else y=y-x; return x; } rat::rat(int x, int y) { if(!y || !x) {ch=0;zn=1;} ch=x; zn=y; this->norm(); }  void rat::norm() {  int sign=1;  if(ch*zn< 0) sign=(-1); ch=abs(ch); zn=abs(zn); int b=nod(ch, zn); if(ch==0 || zn==0) zn=1; if(b==1) return; ch=(ch/b)*sign; zn=zn/b;  } rat rat::operator*(const rat& rvalue) const { rat temp; temp.ch=this->ch * rvalue.ch;  temp.zn=this->zn * rvalue.zn;  temp.norm(); return temp; } rat rat::operator+(const rat& rvalue) const { rat temp; temp.ch=rvalue.zn*this->ch + rvalue.ch*this->zn; temp.zn=this->zn * rvalue.zn;  temp.norm(); return temp; } rat rat::operator-(const rat& rvalue) const { rat temp; temp.ch=rvalue.zn*this->ch - rvalue.ch*this->zn; temp.zn=this->zn * rvalue.zn;  temp.norm(); return temp; } rat rat::operator/(const rat& rvalue) const { rat temp; temp.ch=this->ch* rvalue.zn; temp.hay=this->zn * rvalue.ch;  temp.norm(); return temp; }  istream &operator>>(istream &stream_in, rat &rvalue)  { cout << "Enter rational value (a/b): "; stream_in >> rvalue.ch; stream_in.ignore(); stream_in >> rvalue.zn; cout << endl; return stream_in;  }  ostream &operator<<(ostream &stream_out, rat &rvalue)  { stream_out << "Rational number: " << rvalue.ch << "/" << rvalue.zn <<endl; return stream_out; } 

Давайте разберем код по частям.

Модуль, НОД и конструктор
// abs возвращает x для положительных и -x для отрицательных  int rat::abs(int x) { if(x<0) return (-1)*x; return x; }  // nod() вычисляет наибольший общий делитель x и y int rat::nod(int x, int y) { if(x==0 || y==0) return 1; while(x!=y) if(x>y) x=x-y; else y=y-x; return x; } // конструктор инициализирует рац. число // по умолчанию 0/1 rat::rat(int x, int y) { if(!y || !x) {ch=0;zn=1;} ch=x; zn=y; this->norm(); } 

Напишем функцию norm() для сокращения дроби.

void rat::norm() {  int sign=1;  if(ch*zn < 0) sign=(-1); ch=abs(ch); zn=abs(zn); int b=nod(ch, zn); if(ch==0 || zn==0) hay=1; if(b==1) return; ch=(ch/b)*sign; zn=zn/b;  } 

Идея проста — запоминаем знак дроби, делим знаменатель и числитель на НОД, и умножаем числитель на знак дроби.

Рассмотрим часть кода, в котором перегружается оператор прибавления (бинарный +).

rat rat::operator+(const rat& rvalue) const { rat temp; temp.ch=rvalue.zn*this->ch + rvalue.ch*this->zn; temp.hay=this->zn * rvalue.zn;  temp.norm(); return temp; } 

Заметим, что функция operator+() принимает один параметр типа rat, и возвращает объект типа rat.

Но где же второй операнд для прибавления?
Дело в том, что в выражении А + Б, левый операнд является текущим объектом (*this), а Б — параметром функции operator+().

Если мы перегрузим бинарный оператор как friend, то такого вопроса не возникнет, ибо мы передадим в функцию operator+() и первый, и второй операнд.

Осталось разобраться с операторами >> и <<. Рассмотрим случай с оператором >> (ввод).

istream &operator>>(istream &stream_in, rat &rvalue) { cout << "Enter rational value (a/b): "; stream_in >> rvalue.ch; stream_in.ignore(); stream_in >> rvalue.zn; cout << endl; return stream_in; } 

В функцию operator>>() мы передали два параметра. Первый объект типа istream (поток ввода), а второй — операнд для ввода типа rat.
В данном случае работаем со ссылками, а не прямыми значениями. Функция должна вернуть объект потока.

ссылка на оригинал статьи http://habrahabr.ru/post/177061/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *