C++. Розробка класу, в якому продемонстровано реалізацію виключної ситуації переповнення

Розробка класу, в якому продемонстровано реалізацію виключної ситуації переповнення

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


Зміст


Умова задачі

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


Особливості реалізації

У класі Int, з метою демонстрації реалізовано використання механізму виключень C++ для запобігання переповнення.

Для вбудованого типу int переповнення може виникати у наступних ситуаціях:

  • під час створення об’єкту класу. У конструкторі класу може бути встановлене неправильне значення;
  • під час операції присвоєння деякого значення. Це значення може бути некоректним;
  • під час додавання (віднімання) двох об’єктів типу Int. Сума об’єктів може перевищити максимально (мінімально) допустиме, у результаті чого виникне переповнення;
  • під час множення двох об’єктів типу Int між собою;
  • інші ситуації, які можна реалізувати. Наприклад, множення об’єкту типу Int на деяке число, піднесення об’єкту типу Int в степінь, інкремент, декремент і т.д.

Щоб перехопити переповнення для вбудованого типу int, в класі Int використовується робота з типом long, який має більший діапазон значень (займає більше пам’яті) додатніх та від’ємних величин.

Клас 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 наступна:

#include <iostream>
using namespace std;

// Задача. Б.Страуструп
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 можна реалізувати інші функції.