1 Обработка ошибок

1.1 Традиционные подходы

Техники обработки ошибок

  1. прекратить выполнение
  2. возвратить значение «ошибка»
  3. возвратить допустимое значение и оставить программу в ненормальном состоянии
  4. вызвать функцию обработки ошибок

Вариант 1. Прекратить выполнение программы





char Stack::pop()
{
  assert( top != 0 );
  return store[--top];
}


Результат:


stack_assert: simple_stack.cpp:61:
char Stack::pop (): Assertion ‘top != 0’ failed.
Abort (core dumped)

Вариант 2. Возвратить ошибку




char Stack::pop() {
  return top ? store[--top] : 0;
}




enum tRC {
  OK,
  BAD_SIZE,
  OVERFLOW,
  UNDERFLOW
};
tRC Stack::pop(char* c)
{
  if (!top) return UNDERFLOW;
  *c=store[--top];
  return OK;
}



char Stack::pop(tRC* rc) {
  if (top) { 
    *rc=OK;
    return store[--top];
  }
  *rc=UNDERFLOW;
  return 0;
}


  • Может не быть подходящего значения
  • Результат каждого вызова должен проверяться на «ошибку»

Вариант 3. Оставить программу в ненормальном состоянии


char Stack::pop() {
  if (top!=0) { 
   return store[--top];
  }
  g_Error=UNDERFLOW;
  return 0;
}

int main()
{
  Stack stack;
  char c = stack.pop();
  if (g_Error != OK)  {
    // error occured
  }
  else  {
    // OK
  }
}

Вариант 4. Вызвать функцию обработки ошибок




void* new (size_t size)
{
  for(;;)
  {
    if (void* p = malloc(size))
      return p;
    if (!find_memory_somewhere())
      return 0;
  }
}

1.2 Исключения

Механизм исключений (exceptions):


  • Генерация сообщения об ошибке (throw)
  • Перехват этих сообщений (catch)

В программе может одновременно существовать только одно исключение.



class Stack {
public:
  Stack(int size);
  void push(char c);
  //...
};
class Overflow {};

class WrongSize {
public:
  int wsize;
   Wrong_size(int i):
         wsize(i) {}
};



void Stack::push(char c) {
 if (top<maxSize)
     storage[top++] = c;
 else
    throw Overflow();
}
void f() {
  try {
    Stack s(10);
    s.push('a');
  } catch (Overflow ex)  { 
       cerr << "Stack overflow";
  }
}

Выбор исключений



Stack::Stack(int size)
{
  if ( size > 10000) {
    throw WrongSize(size);
  } //...
}
char Stack::pop()
{
  if (top==0)
    throw Underflow();
  
  return storage[--top];
}



void f(unsigned int size)
{
  try {
     Stack s(size);
     s.push('a');
     char c = s.pop();
     char d = s.pop();
  }
  catch (WrongSize ws) {
    cerr << "Wrong size:" << ws.wsize;
  }
  catch (Overflow) { /*...*/
  }
  catch (Underflow) { /*...*/
  } 
}

Группировка исключений



class Exception {};

class StackError: public Exception {};

class Overflow:  public StackError {};
class Underflow: public StackError {};
class WrongSize: public StackError {};
...
try {
     Stack s(size);
     // ...
}
catch (WrongSize size_exc) {
   // process wronsize exception
}
catch (StackError) {
   // general processing
}

Перехват исключений

Перехват исключении



try {
  //...
}
catch (StackError& se) {
  // process Stack Error
}
catch (RuntimeError& ps) {
  // process Runtime Error
}
catch (Exception) {
 // process Any Internal Exception
}
catch (...) {
  // process any other exception
}

Перехват исключений



try {
  //...
}
catch (...) {
  // Все исключения перехватываются
  // здесь
}
catch (Exception* ex) {
 // process Any Internal Exception
}
catch (RuntimeError* re) {
  // process Runtime Error
}
catch (StackError* se) {
  // process Stack Error
}

Повторная генерация



void f()
{
  try {
    // ...
    throw Underflow();
  }
  catch (RuntimeError& re) {
    if ( can_handle_it_completely(re) ) {
       // process the exception here
       return;
    }
    else {
       do_what_you_can_do(re);
       throw; 
    }
  }
}



void g()
{
  try {
    f();
  }
  catch (StackError& re) {
     // process stack error
  }
  catch (FileError& re) {
     // process file error
  }
}

Исключения в конструкторах

Классические подходы:

  1. Возвратить объект в «неправильном» состоянии
  2. Присвоить значение глобальной переменной
  3. Использовать функцию инициализации
  4. Осуществлять инициализацию при первом вызове функции-члена


Stack::Stack(int i)
{
  if ( (i < MIN_SIZE) ||
       (i > MAX_SIZE) )
  {
    throw WrongSize(i);
  }
  //...
}



Stack* get_stack(int size)
{
  try {
   Stack* s = new Stack(size);
   //...
   return s;
  }
  catch (WrongSize) { 
    // handle bad stack size
  }
}



Объект не создан, пока не завершится выполнение его конструктора



Schedule::Schedule(int i, Date d)
try
 : m_stack(i),
   m_date(d)
{
  // constructor
}
catch (Stack::Bad_Size) {
   // handle bad size of the stack member
}
catch (Date::Bad_Date) {
   // handle bad date of the date member
}

Исключении и копирование

  • Копирующий конструктор подобен другим конструкторам:
    • может генерировать исключения
    • при этом объект не создается
  • Копирующее присваивание перед генерацией исключения должно убедиться, что оба операнда находятся в корректном состоянии

2 Приведение типов

  • dynamic_cast (expr)
  • static_cast (expr)
  • reinterpret_cast (expr)
  • const_cast (expr)

dynamic_cast



void f(Employee* pEmp)
{
   Programmer *pp = dynamic_cast< Programmer*>(pEmp);
   if (pp)
   {
       pp->team();
   }
}
void g(Employee& re)
{
   try {
     Programmer &rp = dynamic_cast< Programmer&>(re);
   } catch (bad_cast) {
       //...
   }
}

static_cast

Преобразование родственных типов




int *p = static_cast< int* >(malloc(100));

enum tColor {RED, GREEN, BLUE};
tColor c = static_cast< tColor >(2);

double d = 2.56;
int i = static_cast< int >(d);

reinterpret_cast

Преобразование несвязанных типов




IO_device *p = reinterpret_cast< IO_device *>(0XffA01);

void* p = allocate_memory_for_programmer();
Programmer *p = reinterpret_cast< Programmer* >(p);

const_cast

Отмена const




void f(const Worker* pw)
{
   Worker *pp = const_cast< Worker*>(pw);

   pp->new_name("Vasya");
}
...
const Worker w("Ivan", "Ivanov");

f(*w); //неопределнное поведение

3 Пространства имен

Пространство имен

  • Является механизмом отражения логического группирования
  • Является областью видимости


namespace My_Lib {
 class Stack {/*...*/};
 void using_stack(int);
 void print_stack(const Stack&);
}
..
void My_Lib::using_stack(int i) 
{
  Stack st(i);
  Chrono::Date date(5, 4, 2004);
  Chrono::display(date);
  print_stack(st);
}



namespace Chrono {
 class Date {/*...*/};
 Date next_date(const Date&);
 void display(const Date&);
}



//chrono.h
namespace Chrono {
  class Date
  {
    int day, month, year;
   public:
     Date (int, int, int);
     void print() const;
  };
  void display(const Date&);
}



//chrono.cc

Chrono::Date::Date(int dd, int mm, int yy)
{
  //...
}
void Chrono::Date::print() const
{
 //...
}
void Chrono::display(const Chrono::Date& rd)
{
    rd.print();
}

Использование using



//namesp.cc
    
int main()
{	
  // using-объявление
  using Chrono::Date;

  Date today(2, 4, 2004);
  Chrono::display(today);

  return 0;  
}



//namesp.cc

// using-директива
using namespace Chrono;
    
int main()
{
  Date today(2, 4, 2004);
  display(today);

  return 0;  
}



// user_interface.h
namespace TcpClient {
 class TcpBaseClient {
 public:
   virtual bool connect(const char* pcHostName, int iPort);
   virtual int write(void* buf, int size)=0;
   virtual int read(void* buf, int size)=0;
 };
 TcpBaseClient* getTcpClient();
}

// prog_interface.h

namespace TcpClient {
 class TcpRealClient: public TcpBaseClient {...};
 class TcpClientHandler {...};
 map<int, TcpBaseClient> TcpClientPool;
 TcpBaseClient* getNextFreeClient();
}

Неименнованные пространства имен

// aa.ccp
namespace {
  int a;
  int f();
}
void g()
{ a = f(); }

// bb.ccp
namespace {
  int a;
  int f();
}

// main.cc

namespace {
  int a;
  int f();
}

int main()
{
  a = f();
}

Псевдонимы пространства имен



namespace American_Telephone_and_Telegraph {
  class Date {/*...*/}
  //...
}

American_Telephone_and_Telegraph::Date d1(5,4,2004);
American_Telephone_and_Telegraph::Date d2(6,4,2004);

namespace ATT = American_Telephone_and_Telegraph;
ATT::Date d3(5,4,2004);
ATT::Date d4(6,4,2004);

namespace Lib = Foundation_library_v2r10;
Lib::Stack s;
Lib::List  l;

Объединение пространств имен



//her_lib.h
namespace Her_lib {
  class String {...};
  String operator+(const String&, const String&);
  String operator+(const String&, const char*);
}

//my_lib.h
namespace My_lib {
  using namespace Her_lib;
  using namespace His_lib;
  
  void my_f(const String&);
}



include "my_lib.h"

void f()
{
  My_lib::String s="hello";
  //...
}

Объединение и отбор



namespace Her_lib {
  class String {...};
  String operator+(const String&, const String&);
  String operator+(const String&, const char*);
}



namespace My_lib {
  using Her_lib::String;
  using Her_lib::operator+;
}



namespace My_lib {
  using namespace Her_lib;
  using namespace His_lib;

  using Her_lib::String;
  using His_lib::Vector;

  void my_f(const String&);
}