try-catch
От: Blitz Великобритания  
Дата: 02.05.04 00:04
Оценка:
Здравствуйте в 225 или какой-там раз =) Спасибо большое за все предыдущие ответы, я очень благодарна вам всем, да и RSDN тоже.

////////////////////////СПАСИБО!!!!!/////////////////

Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

Все эти try — catch... больше всего пугает throw...

Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..

В общем, я уже читала и Шилдта и Страуструпа и STL C++ Reference (microsoft_овскую) и...

НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???

что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


ПОЖАААААААААААЛАААААСТАААААА!!!!!
Re: try-catch
От: MaximE Великобритания  
Дата: 02.05.04 01:39
Оценка:
Здравствуйте, Blitz, Вы писали:

B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...


B>Все эти try — catch... больше всего пугает throw...


[]

http://www.google.com/search?hl=en&ie=UTF-8&oe=UTF-8&q=exception+handling+c%2B%2B&spell=1
Re[2]: try-catch
От: Blitz Великобритания  
Дата: 02.05.04 03:00
Оценка: -1
Здравствуйте, MaximE, Вы писали:

ME>Здравствуйте, Blitz, Вы писали:


B>>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...


B>>Все эти try — catch... больше всего пугает throw...


ME>[]


ME>http://www.google.com/search?hl=en&ie=UTF-8&oe=UTF-8&q=exception+handling+c%2B%2B&spell=1


не смешно.

неужели не понятно, что мне надо "для идиотов" объснение. Если бы и так понятно было....
Re: try-catch
От: Аноним  
Дата: 02.05.04 05:00
Оценка:
Тсс... Не кричи так на весь RSDN!

Exception handling удобен тогда, когда нужно разделить reporting of errors and their handling. Обычно тот, кто пишет функцию может обнаружить ошибку, но не знает, что делать в таком случае. Тот, кто использует эту функцию, знает, что нужно сделать в случае ошибки но не может ее обнаружить (так как не он писал код). Это из Страуструпа.

Например, мы работаем над игрой, и я пишу класс для загрузки 3D моделей. Ты используешь мой класс, и в таком случае я должен тебе как-то дать знать, что произошла ошибка, чтобы ты смогла (как-то корректно) продолжить программу. Можно сообщать об ошибке через глобальную переменную, но ты знаешь, что код почти невозможно читать если их и так в программе какое-то большое количество. Можно вернуть ошибочное значение из функции, но это не всегда удобно, так как трудно порой подобрать значение функции, которое бы указывало на ошибку. Из конструктора класса, например, невозможно вернуть значение вообще!

Итак, я решаю использовать исключения, и мой код выглядит примерно так:
class C3DModel
{
private:
      // Some private data here:
      ...

public:
      class CFileNotFoundException
      {
      private:
            const char *FileThatWasNotFound;

      public:
            CFileNotFoundException(const char *FileName) : FileThatWasNotFound(FileName) { }
            const char *WhichFile() const { return FileThatWasNotFound; }
      };

public:
      // Constructor:
      C3DModel(const char *FileName = NULL)
      {
            // Делаем здесь что-то:
            ...

            // Пытаемся открыть файл и если файл не найден, то "кидаем" исключение:
            int ret = ::TryToOpenFile(FileName);
            if (ret == ERROR)
                    throw CFileNotFoundException(FileName); // Дадим пользователю знать какой файл мы
                                                            // не смогли найти. ТАКЖЕ, БУДЬ ОСТОРОЖНА С
                                                            // throw В КОНСТРУКТОРЕ! МОЖЕТ ПРОИЗОЙТИ RESOURCE
                                                            // LEAK!!!
            // Делаем здесь еще что-то если все в порядке:
            ...
      }

      ~C3DModel() { ... }

      void Load3DModelFile(const char *FileName)
      {
            // Код аналогичен коду в конструкторе...
      }

      void Animate3DModel() const { ... }
};


Твой код будет выглядеть примерно так:
int main()
{
      try {
            C3DModel MyMonster("UglyMonster.3dm");
            MyMonster.Animate3DModel();  // Исполняется если файл был открыт успешно
      }
      catch (const C3DModel::CFileNotFoundException &e) {
            // Эта ветка исполнится если мы не смогли загрузить монстра...

            // Сообщим об ошибке!
            cerr << "Failed to load file " << e.WhichFile() << endl;

            // Сделаем еще что-нибудь тут:
            ...

            return 1;
      }

      return 0;
}
Re: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 02.05.04 05:09
Оценка: 3 (1) +1 :))
Здравствуйте, Blitz, Вы писали:

B>Вопрос у меня довольно пространный, не обессудьте. Но я никак не могу понять как работает exception handling...

Бывает
B>Все эти try — catch... больше всего пугает throw...
Да, особенно это w на конце напоминает усики!
B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..
Иногда это дейтсвительно легче, но иобработку исключений тоже лучше знать.
B>В общем, я уже читала и Шилдта и Страуструпа и STL C++ Reference (microsoft_овскую) и...
Я не думаю, что я дорос до их уровня и авторитета, но попытка не пытка
B>НЕ ВЪЕЗЖАЮ!!!! Не могли бы вы объяснить как-нибудь как пользоваться try-catch???
Постараюсь.
B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???

Для начала, что такое try/catch? Это попробовать и если случилась ошибка поймать её.
try
 {
  // КАКОЙ-ТО КОД, В КОТОРОМ МОЖЕТ СЛУЧИТСЯ ОШИБКА
 }
catch(...)
 {
  // ЛОВИМ ВСЕ ОШИБКИ
 }

Казалось бы такой код эквивалентен следующему
if (не ошибки)
 {
  // ЧТО-ТО СДЕЛАТЬ
 }
else
 {
  // ПРИБРАТЬ ЗА СОБОЙ
 }

Но это только кажется. В чём же разница?

Механизм исключений позволяет отлавливать ошибки место возникновения которых заранее не известно.

Говоря другими слова, когда ты пишешь try, тебе не известно (или всё равно) где именно возникнет ошибка. Главное, что где-то внутри этого try. А если писать с помошью if, то надо самому отследить все возможные места возникновения ошибкок. Вот пример который нельзя переписать с помошью if
void func()
 {
  throw 0; // Собственно сгенерировали исключение.
 }
////////////
void func2()
 {
  try
   {
    func();
   }
  catch
   {
   }
 }

Исключение возникшее в func будет всё равно отловленно.
Далее надо заметить, что кроме программных ошибок (не смогли открыть файл и проч), существуют и аппаратные ошибки (обратились по несуществующему или чужому адресу в памяти). Вот простейщий пример
int * p = NULL;
*p = 3;

В данном случае будет попытка разыменовать указатель который не ссылается на переменную типа int. Это ошибка. Такая ошибка вызовет исключение.

Механизм исключенйи позволяет отсеивать "чужие" ошибки

Ошибки могут быть вызваны разными причинами и иметь раличную смысловую нагрузку. Разделять ошибки на группы, помогает механизм исключений. Это делается на основе того, что исключение имеет тип (как переменная) и разные исключения можно обрабатывать по разному, в зависимости от их типа. То есть когда мы пишем так
catch(...)

Мы отлавливаем все исключения, но когда пишем так
catch(int)
///////////////////////////////
catch(CMyException, int, float)

Мы явно указываем исключения какого типа хотим отлавливать.

Расмотрим пример. Пусть есть класс для обработки группы чисел и класс-хранилище для этих чисел.
Класс хранилище умеет, при нехватке оперативной памяти часть данных сбрасывать на жествкий диск. Но это его внутреннее устройство. Соответственно внутри хранилища есть 2 вида ошибок: не удалось выделить память и не удалось создать файл.
Внутри класса обработки есть два вида ошибок: не удалось загрузить файл и неправильный формат данных.
Вот схематично try/catch этих классов

////////////////////////
Загрузить и обработать данные.
////////////////////////
void DoAllWork()
 {
  try
   {
    LoadData();
   }
  catch(LoaderError)
   {
    throw CanNotLoad;
   }
  Process();
 }
///////////////////////
Загрузить
///////////////////////
void LoadData()
 {
  while (true)
   {
    try
     {
      AllocateMemoryAndReadFile();
      break;
     }
    catch(NoEnoghtMemory)
     {
      try
       {
         WriteSomeDataToHDD();
       }
      catch(IOError)
       {
        throw LoaderError;
       }
     }
   }
 }
///////////////////////
Обработать
///////////////////////
void Process
 {
  if (datalength < 100) throw CanNotProcess;
 }


Рассмотрим продробно. Сперва вызывается LoadData. внутри вызывается AllocateMemoryAndReadFile. Если всё хорошо, тогда break и цикл завершается. Если же произошла ощибка нехватки памяти (NoEnoghtMemory), то управление переходит к WriteSomeDataToHDD. Если записать часть данных не адлось, то мы ловим исключение IOError. Но наверх передаём LoaderError. Соответственно если наверху словили LoaderError, то ещё выше передаётся CanNotLoad. Здесь важно то, что каждый блок try/catch работаеть только с теми исключениями о которых "знает". То есть DoAllWork ничего не знает о внутреннем устройстве LoadData и о том, где и какие ошибки могут возникнуть, но несмотря на это получает понятные ей ошибки. Соответсвенно LoadData не знает как работает AllocateMemoryAndReadFile, но получает ошибку нехватки памяти в понятной форме. ЧТО ВАЖНО! Если внутри AllocateMemoryAndReadFile вызникнет обика вроде FatalError, то на не будет проглочена LoadData и DoAllWork.
То есть благодаря исключениям, мы можем фильтровать ошибки и отлавливать только те, на которые можем адекватно среагировать.
A journey of a thousand miles must begin with a single step © Lau Tsu
Re: try-catch
От: Ассистент Россия  
Дата: 02.05.04 05:42
Оценка:
Здравствуйте, Blitz, Вы писали:

B>Все эти try — catch... больше всего пугает throw...


B>Мне легче дополнительный if(blah-blah-blah){ kill them all } написать, нежели даже пытаться писать с try — catch..


Дело в том, что try — catch предназначен для обработки куска кода, где заранее не известно будет ошибка или нет, а проверить наличие ошибки до выполнения кусочка кода часто сложно.
Достаточно удобно try — catch использовать для передачи ошибки в функцию из которой вызывалась.

Например, разборщик текстов, имеющий:

main(void) - головная функция
parser_text(char *file_name) - парсер всего текста
parser_str(char *str) - парсер отдельной строки


Логика вызова такая: main -> parser_text -> parser_str
В parser_str:
char ss[16]; // строка с ошибкой
int pos=0; // текущая позиция в строке
try{
  // Код потенциально пораждающий ошибку
   ...
  pos++;
}
catch(...){
  // Поймали ошибку, передаем информацию об ошибке вызывающей функции
  itoa(ss,pos,16);
  throw ss;
}


В parser_text:
int pos=0;
char error[50]; // текст ошибки
try{
  // Парсер строки
  parser_str(str);
  ...
  pos++;
}
catch(char * ss){
  // Поймали ошибку, передаем информацию об ошибке вызывающей функции
  // добавляем информацию на данном уровне в переменную error
  ...
  throw error;
}



В main:
try{
  // Парсер строки
  parser_text(str);
}
catch(char *ss){
  // Выводим ошибку
  cout << "Ошибка в строке" << ss;
}
Человека с характером трудности привлекают, потому что только при помощи них он может осознать свой потенциал. (c)Шарль де Голль
Re[2]: try-catch
От: Аноним  
Дата: 02.05.04 06:23
Оценка: 18 (1) +1
A>
A>void func()
A> {
A>  throw 0; // Собственно сгенерировали исключение.
A> }
A>////////////
A>void func2()
A> {
A>  try
A>   {
A>    func();
A>   }
A>  catch
A>   {
A>   }
A> }
A>

Это не C# чтобы так писать! В этом коде синтаксическая ошибка после catch!

A>Механизм исключенйи позволяет отсеивать "чужие" ошибки


A>Ошибки могут быть вызваны разными причинами и иметь раличную смысловую нагрузку. Разделять ошибки на группы, помогает механизм исключений. Это делается на основе того, что исключение имеет тип (как переменная) и разные исключения можно обрабатывать по разному, в зависимости от их типа. То есть когда мы пишем так

A>
A>catch(...)
A>

Ок...

A>Мы отлавливаем все исключения, но когда пишем так

A>
A>catch(int)
A>///////////////////////////////
A>catch(CMyException, int, float)
A>

A>Мы явно указываем исключения какого типа хотим отлавливать.
Вторая строчка — это еще что такое?

A>
A>////////////////////////
A>Загрузить и обработать данные.
A>////////////////////////
A>void DoAllWork()
A> {
A>  try
A>   {
A>    LoadData();
A>   }
A>  catch(LoaderError)
A>   {
A>    throw CanNotLoad;
A>   }
A>  Process();
A> }
A>///////////////////////
A>Загрузить
A>///////////////////////
A>void LoadData()
A> {
A>  while (true)
A>   {
A>    try
A>     {
A>      AllocateMemoryAndReadFile();
A>      break;
A>     }
A>    catch(NoEnoghtMemory)
A>     {
A>      try
A>       {
A>         WriteSomeDataToHDD();
A>       }
A>      catch(IOError)
A>       {
A>        throw LoaderError;
A>       }
A>     }
A>   }
A> }
A>///////////////////////
A>Обработать
A>///////////////////////
A>void Process
A> {
A>  if (datalength < 100) throw CanNotProcess;
A> }
A>

Не в обиду, но думаю, что все это только больше заставит новичка паниковать.
Re[2]: try-catch
От: Кодт Россия  
Дата: 02.05.04 07:02
Оценка:
Здравствуйте, adontz, Вы писали:

<>

Симпатично, но дофига синтаксических ляпов.

Ещё стоит заметить, что ловятся объекты-исключения заявленного типа,
try
{ ... }
catch(some_exception_type ex)
{ ... }

а кидаются-то экземпляры!
throw some_exception_type(ctor-args);

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

Аналогия из обычного программирования — объявление функции с одним аргументом
void catcher(some_exception_type ex)
и её вызов:
catcher(some_exception_type(ctor-args)).
С той лишь разницей, что мы не наматываем, а разматываем стек — т.е. не заходим внутрь catcher(), а выходим до тех пор, пока не найдём соответствующую инструкцию catch().

Пример содержательной обработки исключения:
struct ComException
{
  HRESULT hr_; // экземпляр несёт сведения о подробностях случившегося
  ComException(HRESULT hr) : hr_(hr) {}
};
// удобная функиция-швырялка (см. ниже)
void TestHresult(HRESULT hr) { if(FAILED(hr) throw ComException(hr); }

.....

// функция возвращает нечто полезное: int, но при этом может кинуть исключение.
int fun()
{
  // там, где "плохой" HRESULT является содержательным и подлежит анализу,
  // или там, где нам пофиг - мы не будем выполнять TestHresult.

  CComPtr<IXXX> pXXX;
  TestHresult( pXXX.CoCreateInstance(CLSID_TheXXX) );

  int v;
  TestHresult( pXXX->GetSomeValue(&v) );

  return v;
}

void main()
{
  try
  {
    int v = fun();
    cout << "fun() == " << v << endl;
  }
  catch(ComException ex)
  {
    cout << "fun() reports an error, HRESULT = 0x" << hex << ex.hr_ << endl;
  }
}
Перекуём баги на фичи!
Re: try-catch
От: Аноним  
Дата: 02.05.04 07:07
Оценка:
Здравствуйте, Blitz,
Если попробовать обяснить все простыми словами, то в try надо засовывать те участки кода, где может произойти какой-то сбой. Как правило, когда приходится обращаться куда-то за данными или обьектами, выделять память и пр. Catch — то самое место , куда программа придет, если такое исключение возникнет. Исключение будет сгенерировано и первично обработано ОС. В Catch мы получим и тип исключения, сообщение и пр. Внутри Catch мы решим как его обработать — т.е. наш аварийный план.
Вполне возможно, что где-то, на достаточно высоком уровне мы создали большой центр по обработке исключений. В таком случае мы можем не возится каждый раз с обработкой , а переправлять исключение наверх, в общий центр. Вот для этого и используем trow. Т.е. trow для нас стрелка, которая переправит ошибку в нужное место. При этом мы можем легко доработать сообщение перед отправкой, добавив к нему свое описание ситуации...
Re[3]: try-catch
От: adontz Грузия http://adontz.wordpress.com/
Дата: 02.05.04 07:15
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Это не C# чтобы так писать! В этом коде синтаксическая ошибка после catch!

ДА я собственно не компилировал

А>Вторая строчка — это еще что такое?


Хмм. Мне всегда каазлось, что можно указывать и несколько типов. Пойду поищу откуда я это взял...
A journey of a thousand miles must begin with a single step © Lau Tsu
Re[3]: try-catch
От: sergey_shandar США http://getboost.codeplex.com/
Дата: 02.05.04 07:36
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Ещё стоит заметить, что ловятся объекты-исключения заявленного типа,

К>
К>try
К>{ ... }
К>catch(some_exception_type ex)
К>{ ... }
К>

К>а кидаются-то экземпляры!
К>
К>throw some_exception_type(ctor-args);
К>

К>Это сделано, чтобы можно было передавать в составе исключения разную интересную информацию.

А может лучше так ?
try
{ ... }
catch(some_exception_type &ex)
{ ... }

А то вдруг эту разную интересную информацию потеряем.
getboost.codeplex.com
citylizard.codeplex.com
Re: try-catch
От: Ashen_Angel  
Дата: 02.05.04 08:31
Оценка: -3 :)
Здравствуйте, Blitz, Вы писали:

B>что генерирует throw??? что ловит catch??? что вообще catch делает??? почему не проще написать if(случилось самое страшное){ испугаться и зависнуть}???


Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,
а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.
Re[2]: try-catch
От: achp  
Дата: 02.05.04 11:48
Оценка: +1
Здравствуйте, adontz, Вы писали:

A>
A>int * p = NULL;
A>*p = 3;
A>

A>В данном случае будет попытка разыменовать указатель который не ссылается на переменную типа int. Это ошибка. Такая ошибка вызовет исключение.

Не вызовет. По крайней мере, исключение в смысле Си++ вызвать не должно. В общем случае — вообще не диагностируемо.

MSVC++ "по собственной инициативе" позволяет поймать такое исключение в блоке "catch (...)". Однако, как мы уже выясняли, такое поведение — чистой воды расширение, и конструкция "try-catch" может даже оказаться выброшена в процессе оптимизации, если компилятор увидит, что нет способа, каким внутри блока "try" может быть выброшено исключение в смысле Си++.
Re[3]: try-catch
От: Blitz Великобритания  
Дата: 03.05.04 00:03
Оценка:
Здравствуйте, Аноним, Вы писали:

A>>Мы явно указываем исключения какого типа хотим отлавливать.

А>Вторая строчка — это еще что такое?

A>>
A>>////////////////////////
A>>Загрузить и обработать данные.
A>>////////////////////////
A>>void DoAllWork()
A>> {
A>>  try
A>>   {
A>>    LoadData();
A>>   }
A>>  catch(LoaderError)
A>>   {
A>>    throw CanNotLoad;
A>>   }
A>>  Process();
A>> }
A>>///////////////////////
A>>Загрузить
A>>///////////////////////
A>>void LoadData()
A>> {
A>>  while (true)
A>>   {
A>>    try
A>>     {
A>>      AllocateMemoryAndReadFile();
A>>      break;
A>>     }
A>>    catch(NoEnoghtMemory)
A>>     {
A>>      try
A>>       {
A>>         WriteSomeDataToHDD();
A>>       }
A>>      catch(IOError)
A>>       {
A>>        throw LoaderError;
A>>       }
A>>     }
A>>   }
A>> }
A>>///////////////////////
A>>Обработать
A>>///////////////////////
A>>void Process
A>> {
A>>  if (datalength < 100) throw CanNotProcess;
A>> }
A>>

А>Не в обиду, но думаю, что все это только больше заставит новичка паниковать.

Не, не заставит. Скорее поможет. Примеры — это всегда хорошо.
Re[2]: try-catch
От: Blitz Великобритания  
Дата: 03.05.04 00:08
Оценка:
спасибо за ответы, вроде понятно стало. по крайней мере свой первый try-catch написала вчера, весьма успешно =))))
Re[2]: try-catch
От: Аноним  
Дата: 03.05.04 05:04
Оценка:
A_A>вроде по throw генерируется jmp, а catch — всего-лишь метка, куда надо прыгнуть.
Не может такого быть!
Re[4]: try-catch
От: Аноним  
Дата: 03.05.04 05:09
Оценка:
A>ДА я собственно не компилировал
Так я тоже!

A>Хмм. Мне всегда каазлось, что можно указывать и несколько типов. Пойду поищу откуда я это взял...

Можно, но нужно указывать несколько catch'es подряд или наследовать исключения от какого-то общего класса и затем использовать ссылки или указатели в catch. Например: catch (const some_exception_base_class &e).
Re[3]: try-catch
От: Аноним  
Дата: 03.05.04 05:11
Оценка:
B>спасибо за ответы, вроде понятно стало. по крайней мере свой первый try-catch написала вчера, весьма успешно =))))
Ставь код! Будем баги ловить!
Re[2]: try-catch
От: Willi  
Дата: 03.05.04 06:35
Оценка: +1
Здравствуйте, Ashen_Angel, Вы писали:

A_A>Попробуй почитать асмовский листинг, там всё прозрачно, вроде по throw генерируется jmp,

A_A>а catch — всего-лишь метка, куда надо прыгнуть. Подробности не помню, давно разбирался.

Если бы это было так, то пользы от исключений было с гулькин нос. Потому как хватило бы обычных, setjump / longjump.
Основная фишка в том, что при раскрутке стека вызываются деструкторы локальных объектов.
Это позволяет автоматически освобождать захваченные ресурсы, при условии, что человек использует "умные" указатели и т.п.
\/\/i||i
Re[3]: try-catch
От: Ashen_Angel  
Дата: 03.05.04 15:49
Оценка:
Здравствуйте, Willi, Вы писали:

Всё немного сложнее, чем написал я, но в целом я прав.

W>Если бы это было так, то пользы от исключений было с гулькин нос. Потому как хватило бы обычных, setjump / longjump.

W>Основная фишка в том, что при раскрутке стека вызываются деструкторы локальных объектов.
W>Это позволяет автоматически освобождать захваченные ресурсы, при условии, что человек использует "умные" указатели и т.п.


Думаешь я не смогу реализовать это через обычные jump'ы?
Просто при использовании throw/catch это сделает компилятор.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.