C++. Разработка класса, демонстрирующего реализацию исключительной ситуации переполнения

Разработка класса, демонстрирующего реализацию исключительной ситуации переполнения

В данной теме рассмотрен пример того, как можно реализовать класс, который обрабатывает исключительную ситуацию переполнения с помощью инструкции try…catch. По данному примеру можно научиться разрабатывать собственные классы, которые используют механизм исключений C++ для обработки исключительных ситуаций.


Содержание


Условие задачи

Разработать класс Int, который ведет себя как встроенный int, только он еще генерирует исключение в случаях переполнения. Условие задачи взято из книги Б. Страуструпа «Язык программирования C++».


Особенности реализации

В классе Int, с целью демонстрации реализовано использование механизма исключений C++ для предотвращения переполнения.

Для встроенного типа int переполнение может возникать в следующих ситуациях:

  • во время создания объекта класса. В конструкторе класса сразу может быть установлено неправильное значение;
  • во время операции присваивания некоторого значения. Это значение может быть некорректным;
  • во время суммирования (вычитания) двух объектов типа Int. Сумма объектов может превысить максимально (минимально) допустимое, в результате чего возникнет переполнение;
  • во время умножения двух объектов типа Int между собой;
  • другие ситуации, которые можно реализовать. Например, умножение объекта типа Int на некоторое число, возведение объекта типа Int в степень и т.д.

Чтобы перехватить переполнение для встроенного типа int, в классе Int используется работа с типом long, который имеет больший диапазон значений (занимает больше памяти) положительных и отрицательных величин. Соответственно можно производить необходимые проверки на превышение максимально (минимально) допустимого значения типа int.

Класс Int реализован для 16-разрядных значений типа int.

 


Выполнение

С целью демонстрации, в классе Int реализованы следующие функции:

  • конструктор по умолчанию;
  • конструктор с одним параметром. В конструкторе используется инструкция try…catch для проверки на корректность значения параметра;
  • операторная функция operator=(Int&), которая перегружает оператор присваивания =. Эта функция используется для присваивания объектов типа Int;
  • перегруженная операторная функция operator=(long), которая перегружает оператор присваивания =.   Эта функция используется для присваивания некоторого значения объекту класса Int. В этой функции используется блок try…catch;
  • операторная функция operator+(Int), перегружающая оператор суммирования +. В этой функции используется блок try…catch;
  • операторная функция operator*(Int), перегружающая оператор умножения *. Функция использует механизм try…catch для проверки корректности значения результата умножения;
  • глобальная «дружественная» (friend) операторная функция operator<<(), которая перегружает оператор вывода в поток <<. Функция предназначена для вывода объектов класса Int. Функция реализована с целью демонстрации;
  • глобальная «дружественная» (friend) операторная функция operator>>(), которая перегружает оператор >> ввода из потока. Функция предназначена для ввода объектов класса Int и реализована с целью демонстрации.

Для определения максимально допустимого и минимально допустимого значения встроенного типа int, в классе Int используются константы INT16_MIN и INT16_MAX. По желанию можно выбрать другие константы (например INT32_MIN, INT32_MAX).

Реализация класса Int следующая:

// Задача. Б.Страуструп
class Int
{
private:
  long value;

public:
  Int() { value = 0L; }

  // Конструктор класса
  Int(long _value)
  {
    try // проверка на переполнение
    {
      if ((_value < INT16_MIN) || (_value > INT16_MAX))
        throw "Constructor. Exception: overflow.";
      value = _value;
    }
    catch (const char* e) // перехват исключения
    {
      // обработать исключение
      value = 0L;
      cout << e << endl;
    }
  }

  // Операторная функция, перегружающая оператор присваивания =,
  // эта функция имеет две перегруженные реализации
  // Реализация 1.
  Int& operator=(Int& t)
  {
    // Здесь проверки на переполнение не нужно,
    // поскольку значение в t уже не содержит переполнения
    value = t.value;
    return *this;
  }

  // Реализация 2.
  Int& operator=(const long _value)
  {
    // В этой реализации операторной функции обязательно нужна проверка
    try {
      if ((_value < INT16_MIN) || (_value > INT16_MAX))
        throw "Assignment operator=(long). Exception overflow.";
      value = _value;
    }
    catch (const char* e) // перехват исключения
    {
      cout << e << endl;
    }
    return *this;
  }

  // Перегрузка оператора суммирования +
  Int& operator+(Int i)
  {
    long t = value+i.value;
    try {
      if ((t < INT16_MIN) || (t > INT16_MAX)) // проверка на переполнение
        throw "Addition: overflow. ";
      value = t;
    }
    catch (const char* e) // перехват исключения
    {
      cout << e << endl;
    }
    return *this;
  }

  // Перегрузка оператора умножения,
  // здесь нужна проверка на переполнение
  Int& operator*(Int i)
  {
    long t = value * i.value;
    try {
      if ((t < INT16_MIN) || (t > INT16_MAX))
        throw "Addition: overflow. ";
      value = (long)t;
    }
    catch (const char* e)
    {
      cout << e << endl;
    }
    return *this;
  }

  // Перегрузка операторов <<, >>
  friend std::ostream& operator<<(std::ostream& stream, const Int& i);
  friend std::istream& operator>>(std::istream& stream, Int& i);
};

// Глобальная функция, перегружающая оператор вывода <<
std::ostream& operator<<(std::ostream& stream, const Int& i)
{
  stream << i.value;
  return stream;
}

// Глобальная функция, перегружающая оператор ввода >>
// здесь параметр Int& i не константный
std::istream& operator>>(std::istream& stream, Int& i)
{
  long t = i.value; // сохранить старое значение
  try {
    stream >> i.value;
    if ((i.value < INT16_MIN) || (i.value > INT16_MAX))
      throw "Input exception: overflow.";
    return stream;
  }
  catch (const char* e)
  {
    // Обработка исключения. Восстановить старое значение
    i.value = t;
    cout << e << endl;
  }
}

void main()
{
  // Тест для класса Int
  Int i = 500000; // в конструкторе генерируется исключение
  Int t = 300; // исключение не генерируется

  cout << "i = " << i << endl;
  cout << "t = " << t << endl;

  cout << "i = t" << endl;
  i = t; // вызов операторной функции operator=(Int&)
  cout << "i = " << i << endl;

  i = 500;
  cout << "i = " << i << endl;

  cout << endl << "Input i: ";
  cin >> i;
  cout << "After input: i = " << i << endl;

  // оператор присваивания operator=(int)
  cout << "--------------------" << endl;
  cout << "i = 50000" << endl;
  i = 50000;
  cout << "i = " << i << endl;

  i = t = 3000;
  cout << "-----------------" << endl;

  cout << "i = t = 3000" << endl;
  cout << "i = " << i << endl;

  cout << "t = " << t << endl;
  cout << "------------" << endl;

  cout << "25000+15000 = 40000" << endl;
  i = 25000;
  t = 15000;
  t = t + i;
  cout << "t = " << t << endl;
  cout << "------------" << endl;

  cout << "i = 5; t = 220;" << endl;
  i = 5;
  t = 220;
  cout << t * i << endl;
}

По данному примеру в классе Int можно реализовать и другие функции.