Раздел «Язык Си».OOP-Instrumental_a:

Перегрузка методов. Перегрузка операторов.

Перегрузка методов (функций)

В С++ в одном пространстве имен можно переопределять функциональность методов с одним и тем же именем, но с разным набором параметов.

REFACTOR Рассморим задачу

Дробь задается двумя положительными целыми числами, не превышающими 232 - 1. Требуется реализовать инструментарий работы с дробями.

Сначала реализуем возможность задавать дробь сразу при ее создании и различные методы печати этой дроби.

#include <iostream>
#include <cstdlib>
using namespace std;

class Drob{
 int cel, chisl,znam;

public:
  Drob(); // конструктор дроби по-умолчанию
// Инициализирующий конструктор  
  Drob(int,int,int);
// Устанавливаем значение  
  void setVol(int, int,int);
// Печать дроби в формате простой дроби
   void print();
// Печать дроби в десятичном виде (n - количество знаков после запятой)
   void print( int n); 
};
// Конструктор "по-умолчанию"
Drob::Drob(){
  cel = chisl = 0;
  znam = 1; 
};
// Инициализирующий конструктор (с параметрами )
// По количеству и типам параметров С++ различает какую из 
// какую из имеющихся функций нужно применять
Drob::Drob(int c, int ch, int z){
     cel = c;
     chisl = ch;
     znam = z;
};

// "Обычный" print()
void Drob::print(){
   cout<<cel<<'('<<chisl<<'/'<<znam<<')';
};

// print() "с парамером" - n означает сколько знаков 
// после запятой нужно напечатать.
void Drob::print( int n){
   int c = cel * znam + chisl;
   int b = znam;
  for(int i = 0; i < n + 1; i++){
   int pr = c / b;
   cout<<pr;
   if (i == 0)
    cout<<'.';
   int next = c % b;
   next *= 10;
   while(next < b){
     next *= 10;
     i++;
     if (next >= b || i>=n) break;
     cout<<'0'; 
   }
  c = next;
 }
  cout<<endl;
};  

// Примеры использования конструктора и переопределенных 
// операторов
int main(){
  Drob a; // конструктор "по-умолчанию"
  Drob b(1,2,3); // инициализирующий конструктор
  a.print();// "обычный" print()
  b.print(7);// print() "с парамером" (7 знаков)

 return 0;
}

При работе с математическими объектами и в ряде других случаев перегрузка операторов очень полезна, так как для зарезервированный в языке операторов сохраняются все их свойства: разбор скобочных структур, приоритет и т.д.

Задачи

REFACTOR Задача 1

Реализовать для работы с простыми дробями необходимо:
  1. исправить печать дроби в формате <целая часть>(<числитель><знаменатель>). Если цела часть отсутствует, она не печатается, если знаменатель равен 1, печатается как целое, если отсутствует дробная часть или числитель равен 0, дробная часть не печатается. Дробь должна быть представлена в несократимом виде.
  2. реализовать void Drob::print(char); - если указана * - печать числа в стандартном виде: целая часть предсавляется одной цифрой, далее точка, далее непереодическая часть дроби, затем периодическая в скобках, знак умнжения ('x') и десятичная степень, на которую нужно умнжить число, чтобы получить правильное. Например, представление дроби 5/7: 0.(714285)x0, то есть 0.(714285)x100

    Переопределение операторов

    Продолжим рассмаривать задачу с дробями.

    В С++ можно переопределять сществующие в языке операторы: +, - , /, *, ==, ! и др. Операторы бывают: "бинарные"; "унарные" - "префиксные", "постфиксные" и др.

    Дроби, очевидно, удобнее складывать как обычные числа. постараемя переопределить операторы +, ++ - увеличение на 1 и ! - получение обратоной дроби.

    #include <iostream>
    #include <cstdlib>
    using namespace std;
    
    class Drob{
     int cel, chisl,znam;
    
    public:
      Drob(); // конструктор дроби по-умолчанию
    // Инициализирующий конструктор  
      Drob(int,int,int);
    // Устанавливаем значение  
      void setVol(int, int,int);
    /*
    Зарезервированное слово "operator" обозначает описание именно
    оператора, а не какой-либо другой функции.
    В С++ существуют два способа передачи адреса: указатель (*) и 
    ссылка (&).
      При передачи указателя синтаксис работы с этим объектом 
    как с указателем (разыменовывание - *point, обращение к 
    атрибутам и методам - point->metod()).
    
      При передачи ссылки сохраняется синтаксис как при работе с 
    самим объектом: разыменовывание не нужно.
    
    Но объявить ссылку как отдельный объект нельзя. Она 
    используется только для передачи данных в функции и как 
    возвращаемые значения функций.
     
    При передачи адреса есть опасность несанкционированно 
    изменить значение объекта. Чтобы этого не произошло, 
    объявляется const. Тогда такие попытки будут отловлены на 
    стадии компиляции.
      
    */
      Drob operator+(const Drob&); 
    
    /*
      Оператор ++ - постфиксный, то есть пишется после объекта. 
    Поэтому для соблюдений правил синтаксиса ему нужно указать 
    неиспользуемый параметр int
      
    Так как этот оператор изменяет текущий объект, он возвращает 
    указатель на себя самого. Для того, чтобы иметь указатель на текущий 
    объект, имеется специальное слово this.
    */
      Drob& operator++(int);
      
    
    /*
      Оператор ! - префиксный, то есть пишется перед объектом. 
    Ему не нужно указывать дополнительный параметр, так как сам 
    объект формально является его параметром.
       
      Для всех операторов существует строго определенное количество 
    параметров, а для некоторых параметров еще должен сохраняться его 
    тип. Переопределяя оператор, нельзя изменить количество параметров 
    для этого оператора (а для некоторых еще и тип). Переопределению 
    подлежит, в основном, функциональность.
    */
    
      Drob& operator!();
    
    // Для отладки
      void print();
      void print(char);
      void print(int);
    };
    
    Drob::Drob(){
      cel = chisl = 0;
      znam = 1; 
    };
    
    void Drob::print(){
       cout<<cel<<'('<<chisl<<'/'<<znam<<')';
    };  
    
    void Drob::setVol(int a, int b, int c){
         cel = a;
         chisl = b;
         znam = c;
    };  
    
    /*
     Это "заглушка" - то есть функциональность 
    реализована не полностью.
    Этот метод  - только как пример написания подобных.
    
    При использовании параметра, переданного "по-ссылке" синтаксис
    использования такой же как и для параметров, переданных как копии
    */
    Drob Drob::operator+(const Drob& a){
    Drob tmp;
         tmp.chisl = chisl + a.chisl;
         tmp.znam = znam;
         return tmp;
    };
    
    /*
      Это также "заглушка". 
    Изменения произошли с текщим объектом, и 
    возвращается указатель на текущий объект.
      Указывается параметр int, который не используется.
    */
    
    Drob& Drob::operator++(int){
       cel++; 
    
       return *this;
    };  
    
    /*
      Префиксный оператор. 
    Также возвращается ссылка на текущий объект.  
    */
    Drob& Drob::operator!(){
        int z = chisl + cel;
        chisl = znam;
        znam = z;
        cel=0;
        return *this;
    };
    
    int main(){
      Drob c;
      Drob a(0,2,5), b(0,1,5);
    
     
    // Пример использования оператора +
      c= a + b;
    // Выше была показана краткая запись (рекомендуется)
    // Полный вызов выглядит так: с = a.operator+(b);
    
      c.print();
    // Пример использования инкремента.  
      c++;
      c.print();
    // Пример использования инвертирования.  
      !c;
      c.print(*);
      
    }  
    

    Задачи

    REFACTOR Задача 2

    Реализовать следующие операторы для работы с классом "Дроби":
    1. полноценные операции сложения, вычитания, умножения, деления, инвертирования дробей (например, сложение и дробью, и с числом)
    2. операции сравнения дробей: равенство, больше, меньше (так же и с дробью, и с числом). Если истина, возвращаем 1, ложь - 0

    REFACTOR Задача 2a Интервалы времени

    Написать два класса:

    class TimeInterval{
     CTime begin; // начало промежутка
     CTime end;  // конец промежутка
     public:
      TimeInteraval(); 
      TimeInterval(CTime, CTime);
    // Сравнение двух промежутков времени:
    // если не пересекаются, возвращаем 0
    // если пересекаются 
    // если полностью совпадают, возвращаем 10
    // если первый полностью входит во второй — 12
    // если второй входит в первый — 13
    // если начало первого раньше — 14
    //  если начало второго раньше — 15
    // если конец первого позже — 16
    //  если конец второго позже — 17
      int operator>(const TimeInterval);
     // печать в поток 
      ostream& put(ostream&);
      friend class timeLine;
    };
    
    class TimeLine{
    // список интервалов.
    // все интервалы должны быть упорядочены по времени
    //  и не пересекаться.
    // Если два интервала пересекаются, то они преобразуется в один интервал,
    // началом которого выбирается самое ранне время, а концом — самое позднее
    // если пересекаются более двух интервалов, то началом выбирается 
    // самое раннее время, а концом самое  позднее
    // Например: были интервалы (10:00-12:00, 13:00-13:20, 14:05-14:10)
    // Добавляем интервал (11:00-14:00)
    // В результате получает такой список: (10:00-14:00, 14:05-14:10)
      TimeInterval *timeLn;
      int n;
    public:
       TimeLine();
    // Добавить интервал. Правила образования интервалов как
    // описано выше
       TimeLine& operator+=(TimeInterval);
    // Удалить интервал. 
    // Если этот интервал не пересекается ни с каким,
    // то список не изменяется.
    // Если пересекается, то из списка удаляется пересекаемая часть
    // Например: список: (10:00-14:00, 14:05-14:10), удаляем интервал (11:00-12:00)
    // получаем (10:00-11:00, 12:00-14:00, 14:05-14:10)
       TimeLine& operator-=(TimeInterval);
    // Добавить еще один список. Интервалы при этом 
    // получаются по правилам, описанным выше
       TimeLine& operator+(const TimeLine&);
    //  удалить список интервалов. Правила
    // образования новых интервалов описаны выше
       TimeLine& operator-(const TimeLine&);
    // печать списка на поток.   
       ostream& put(ostream&);
    };

    REFACTOR Задача 3 Воcхождение

    Крокодил Гена и Чебурашка совершают восхождение на гору. Чебурашка едет в рюкзаке за спиной у Гены. Гена все время падает и скатывается вниз из-за Чебурашки. Он рассказывает смешные анекдоты. Перед началом восхождения Гена тоже упал. После того, как они проходят 1/n часть расстояния до вершины, Чебурашка рассказывает Гене анекдот, и тот от смеха вместе с Чебурашкой скатывается вниз на 1/k часть пройденного расстояния от того места куда он в прошлый раз скатился. И всякий раз, как только Гена поднимается на 1/n часть расстояния отделяющего место, куда он скатился, от вершины, Чебурашка рассказывает очередной анекдот, и Гена вновь скатывается на 1/k пройденного с этого места пути. Последний (m-ный) анекдот Чебурашка рассказал, когда до вершины остался 1 метр, но этот анекдот оказался настолько смешным, что на этот раз Гена скатился к самому подножью горы, и на этом восхождение закончилось. Написать программу, которая вычисляет высоту горы и выдает ТОЧНЫЙ результат в виде смешанной дроби, не округляя и не приводя ее к десятичной. Дробная часть записывается в круглых скобках, числитель отделяется от знаменателя косой чертой. Например 102(71/93) обозначает высоту горы

    -- TatyanaOvsyannikova2011 - 17 Feb 2016