Здравствуйте, Borisman, Вы писали:
B>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>1) throw CMyException(...) B>2) throw new CMyException(...)
B>Бойцы, у кого какое мнение?
В пользу второго способа, imho, есть только один разумный довод (производительность я не считаю, ибо ей с исключениями не по пути), с которым я буквально сегодня столкнулся. Если исключение выбрасывается с помощью способа №1 из .dll, а ловится, к примеру, в .exe, причем при раскрутке стека та самая .dll выгружается, то произойдет Access Violation в catch-блоке. В таком случае, лучший вариант -- выбросить указатель на исключение, а не экземпляр. Я обошел эту ситуацию, обернув код выгрузки .dll условием if(!std::uncaught_exception())
Огромный плюс в первом способе выброса исключения в том, что так ведет себя стандартная библиотека. Поэтому, если придется использовать вариант №2, то возникнут проблемы с отловом ее исключений, а также многих других библиотек (boost, например)
Здравствуйте, Аноним, Вы писали:
А>Хотелось бы отметить, что у передачи объекта-исключения указателем есть и определенные преимущества (не умаляя недостатков ).
А>Например, при передаче значением выполнить что-то вроде InnerException в .Net (т. е. обернуть одно исключение другим, так что исходное исключение становится причиной исключения на более высоком уровне абстракции) можно, но становится некрасиво.
Когда я впервый прочитал и понял, зачем в .NET нужен InnerException, я задумался, как же я всю жизнь обходился ьез InnerEcxeption. Вообще всю эту микробучу на форуме я затеял потому, что сейчас пытаюсь придумать свою библиотеку исключений под С++ где была бы поддержка InnerException. Есть по сути два варианта:
1) Либо бросать указатели на динамически создаваемые исключения и иметь затем проблемы с их удалением (зато быстро работает).
2) Либо бросать копии исключений и обертывать их другими исключениями и иметь проблемы сос скоростью работы (зато никаких проблем с удалением исключений).
Лично мне второй вариант кажется предпочтительнее. Исключения на то и исключения, чтобы происходить сравнительно редко. Вообще обработка любого исключения — дело накладное и не особо тут выиграешь на кописровании классов.
Под влиянием общественности пришел в голову третий вариант — можно кидать объекты, ловить ссылки и вот эти ссылки(а не указатели) и оборачивать другими исключениями. Вот так надо и сделать, по-моему.
К>Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому
В>void InnerCheck2( a )
В>{
В> if( a == 1 )
В> throw"oooops";
В> throw L"OOOOPS";
В>}
В>void InnerCheck1( a )
В>{
В> InnerCheck2(a);
В> throw (int)a;
В>}
В>void SomeCheck( a )
В>{
В> InnerCheck1(a);
В>}
В>
В>void SomeFunction
В>{
В> try
В> {
В> for()
В> ...
В> for()
В> {
В> SomeCheck( a );
В> }
В> }catch( int e )
В> {
В> printf( "some var1 = %d", i );
В> }catch( char* e )
В> {
В> printf( " some var2 = %s",e );
В> }
В> catch( wchar_t* e )
В> {
В> printf( " some var2 = %s",e );
В> throw e;// перекинули на обработку дальше
В> }
В> printf( "final" );
В>}
В>
Забавно. Но по-моему кидать что-то типа
throw"Приветик из Австралии!"
весьма надуманно. Ну кому может понадобиться такой приветик? Кидают как правило что-нить вроде CArgumentException("IRQ_LEVEL", 0, 1) и в самых крайних случаях.
Я все это говорю к тому, что спор о том, что лучше — throw или goto достаточно бессмысленный. Исключения должны кидаться в ИСКЛЮЧИТЕЛЬНЫХ случаях, а не для выхода из каких-то там вложенных циклов. Если ситуация, когда нужно выйти из кучи вложеннных циклов (или там, функций) — достаточно типичная, пусть даже ошибочная, и часто происходящая, то не следует по-моему пользоваться исключениями. Выйдите как все нормальные люди выходят — через дверь (я имею в виду старый добрые операторы break, return и т.д. Насчет goto — я лично против. Вот уже 5 лет не написал ни одного оператора goto и прекрасно себя чуствую).
Типичный пример, который вычитан мной из МСДН.
Пользователь должен авторизироваться при коннекте к серверу. Можно, конечно, сделать так:
Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться.
Лучше все-таки по старинке:
sev>После некоторых раздумий я реализовал решение, коим весьма теперь доволен. sev>Все проблемы с автоматическим удалением исключения в нём решены, а также sev>добавлена возможность "вложенности" исключений в стиле .NET.
sev>Решение можно посмотреть в исходном тексте вот здесь: sev>http://www.demoforge.com/misc/SmartExceptions.h
Да, действительно неплохо! Недостатка на самом деле два — второй — использование СException, a, значит, привязка к MFC. Но иначе все дела со снимком стека пришлось бы самому кодить. Очень, очень неплохо, мне понравилось. Именно что-то подобное я и хотел сам сделать, потому и вопрос такой задал.
Здравствуйте, SchweinDeBurg, Вы писали:
SDB>Здравствуйте, Borisman, Вы писали:
B>>...использование СException, a, значит, привязка к MFC...
SDB>Там же свой CException определяется, причем здесь MFC?
Да, беру свои слова обратно. Есть там CException. Но там и CString есть, хотя это уже не принципиально — можно в конце концов подправить чуток и обойтись без MFC. Я так и сделаю, если автор будет не против.
Здравствуйте, Borisman, Вы писали:
B>...Но там и CString есть...
Есть у меня сильное подозрение, что CString там тоже авторский. Хотелось бы, конечно, услышать комментарии самого автора.
Здравствуйте, Borisman, Вы писали:
B>Типичный пример, который вычитан мной из МСДН. B>Пользователь должен авторизироваться при коннекте к серверу. Можно, конечно, сделать так:
B>
B>Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться. B>Лучше все-таки по старинке:
Чем глупо?
Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.
"We charge!"
Re[6]: Зачем надо throw new CMyException
От:
Аноним
Дата:
12.03.03 07:36
Оценка:
Здравствуйте, Borisman, Вы писали:
B>Под влиянием общественности пришел в голову третий вариант — можно кидать объекты, ловить ссылки и вот эти ссылки(а не указатели) и оборачивать другими исключениями. Вот так надо и сделать, по-моему.
Не выйдет. В catch-блоке ты получаешь ссылку на временный, в сущности, объект. Сохранение ссылки на него — все одно, что сохранение ссылки на автоматический объект после выхода его из области видимости.
B>Но концептуально это глупо. Ведь неверное набирание пароля — дело лостаточно обычное, чего тут за голову хвататься и исключениями кидаться. B>Лучше все-таки по старинке:
B>
B>Каждый инструмент должен использоваться по назначению.
НА мой взгляд в некоторых случаях наоборот. К примеру, если пароль проверяется в более глубоких функциях, то проверять каждый раз возвращяемое значение не особо читабельно, можно забыть его проверить, помнить, что кадый нумер ошибки значит и отдельно его обрабтывать. А исключение кинул "неверное имя или пароль" и тебя больше не волнует, а вдруг кто то заюыл проверить возвращяемый результат. Оно обработалось обработчиком нужного типа и т.п. и т.д.
Здравствуйте, Areex, Вы писали:
A>Чем глупо? A>Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.
Ошибка — тоже результат.
Тут нет, конечно, единого правила. Но все-таки не следует часто случающиеся ошибки кодировать исключениями. Все надо делать в соответствии со зравым смыслом, вот только здравый смысл у каждого свой
Здравствуйте, Borisman, Вы писали:
B>Здравствуйте, Areex, Вы писали:
A>>Чем глупо? A>>Именно так и надо делать. Концептуально функция должна возвращать результат, а не сообщение об ощибке.
B>Ошибка — тоже результат.
Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.
И возникает головная боль со всякими кодами ошибок и прочими GetLastError.
B>Тут нет, конечно, единого правила. Но все-таки не следует часто случающиеся ошибки кодировать исключениями. Все надо делать в соответствии со зравым смыслом, вот только здравый смысл у каждого свой
Ты не аргументировал, почему не следует. Обработка ошибок прямое назначение исключений. Этак половину c++ можно не использовать, мало ли у кого какой здравый смысл.
Здравствуйте, Ведмедь, Вы писали:
К>>Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому интересно, насколько быстро моя программа может сбоить???
В>Кстати не совсем верно. В данном вопросе да, производительность значения не имеет. Я тоже считаю, что если произошло исключение, то обработать его в 90 процентов случае время есть и можно уже не торопиться Но факт в том что try/catch не в лучшей сторону влияет на производительность, не зависимо от того, происходит или нет исключение. А вот это не приятно.
Я так и не понял, что не совсем верно?
Кстати, скорость алгоритма при использовании try-catch падает не немножко, а существенно. Я не так давно пробовал раскрутить стек с помощью исключений в одном большом проекте. Было место, где этот подход выглядел сипатичнее и если разруливать ситуацию стандартно, то надо было писать много различных проверок (во многих местах this мог стать вдруг инвалидным). Попробовал.
Этот блок стал занимать 12% от времени работы всего алгоритма! (измерял TrueTime-ом)
Конечно, я его убрал. И зарекся от использования исключений без надобности.
Так что, не наступайте на грабли
sev>>Решение можно посмотреть в исходном тексте вот здесь: sev>>http://www.demoforge.com/misc/SmartExceptions.h > Да, действительно неплохо! Недостатка на самом деле два — второй - > использование СException, a, значит, привязка к MFC. Но иначе все дела со > снимком стека пришлось бы самому кодить. Очень, очень неплохо, мне > понравилось. Именно что-то подобное я и хотел сам сделать, потому и вопрос > такой задал.
СException там свой, если кого волнуют конфликты с MFC (я его не использую),
то можно погрузить это всё в namespace. CString взят из ATL/MFC 7.
Вообще говоря, структура самого СException здесь непринципиальна,
главное, что есть иерархия этих СException (соответственно, необходим
виртуальный деструктор) и дублирующая иерархия СExceptionPtr, чтобы
обеспечить полиморфизм в catch(). На самом деле СException это даже
не exception в том смысле, что его объекты никогда не бросаются
Касаемо new: подразумевается, что используется run-time с бросающим
new. Т.е. std::bad_alloc нужно ловить в отдельном catch, либо можно
унаследовать CExceptionPtr от std::exception, тогда ловить их все в одной
куче, но при этом не будет доступа к CException. В общем, не самая удачная
идея. Я предпочитаю реагировать на std::bad_alloc выбросом сообщения
"Шеф, усё пропало!" и не утруждать себя дальнейшей диагностикой.
Главное, что если мы собирались бросить исключение, то оно всё равно
бросится, даже если не сможет сконструироваться, правда в этом случае
уже бросится уже несколько другое исключение
> Вообще, sev, вот Вам еще пища для размышлений: > http://oop.rosweb.ru/users/v/vbelkin/yaace.htm > Почитайте, думаю Вам будет интересно.
Хм, весьма остроумно Особенно мне понравилась идея с выяснением
типа исключения, пойманного через catch (...). Перечитал стандарт, вроде
ничему не противоречит.
Правда, я не вижу смысла (для себя) решать те проблемы, которые перед
собой поставил автор этой статьи.
Здравствуйте, sev, Вы писали:
sev>После некоторых раздумий я реализовал решение, коим весьма теперь доволен. sev>Все проблемы с автоматическим удалением исключения в нём решены, а также sev>добавлена возможность "вложенности" исключений в стиле .NET.
sev>Решение можно посмотреть в исходном тексте вот здесь: sev>http://www.demoforge.com/misc/SmartExceptions.h
sev>Теперь минусы: единственным отрицательным моментом, на мой взгляд, является sev>то, что для каждого нового класса исключений нужно не забыть сделать один sev>дополнительный typedef, благодаря которому появляется возможность пользоваться sev>приведением к базовому классу в catch. В вышеприведённом коде видно, что мы sev>ловим более абстрактное исключение, чем выбросили.
sev>Любые усовершенствования приветствуются
Мне кажется, что писать что-то вроде
throw CMyExceptionPtr(new CMyException(...));
довольно неудобно. Вместо этого может имеет смысл сделать специальную функцию
При этом, конечно, придется делать такой typedef для inherited (базового исключения) в определении исключения, но тогда можно избавиться от того "typedef'а в минусах".
Здравствуйте, Ведмедь, Вы писали:
В>Ну не всегда. в некоторых случаях IMHO прозрачней как раз через try/catch ( например, выход из N вложенных циклов )
тут уже был здоровенный флейм на эту тему :)))
Можно сказать, легенда RSDN :)))
Здравствуйте, MaximE, Вы писали:
ME>Здравствуйте, grs, Вы писали:
Нда. Похоже у меня в последнее время туго с выражением собственных мыслей. Так всегда бывает, когда занимаешься 4 делами одновременно.
Короче в том постинге, где ты мне 0 влепил я хотел сказать лишь следующее:
При выкидывании исключения нужно всегда использовать throw CMyExecption, а не throw new CMyExeption, а в блоке нужно писать catch(CMyExeption&), а не catch(CMyExeption) или catch(CMyExeption*). Иначе козленочком станешь. Рано или поздно. Только и всего. Хотя написал коряво, признаю.
sev>А ниже показано, как это решение работает "в жизни".
sev>void ThrowingFunc() sev>{ sev>throw CSystemExceptionPtr(new CSystemException(E_FAIL)); sev>}
sev>Самое главное, что код абсолютно безопасен (я так думаю во всяком случае
А я не понял, а что будет если при вызове ThrowingFunc new выкинет скажем std::bad_alloc? Все-таки не нравится мне эта идея с динамическим выделением памяти при генерации исключения. Проще надо ИМХО, конечно.
A>Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.
То, что для одного ошибка — для другого — проза жизни . Это смотря с какой стороны посмотреть. Я лишь говорю о том, что нет единого правила, говорящего о том, следует применять обработку исключений или нет. Но является ли неправильный пароль ИСКЛЮЧИТЕЛЬНОЙ ситуацией? Сколько раз из ста пользователь ошибается при наборе пароля?
A>Ты не аргументировал, почему не следует. Обработка ошибок прямое назначение исключений. Этак половину c++ можно не использовать, мало ли у кого какой здравый смысл.
Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?