Здравствуйте, Borisman, Вы писали:
B>То, что для одного ошибка — для другого — проза жизни . Это смотря с какой стороны посмотреть. Я лишь говорю о том, что нет единого правила, говорящего о том, следует применять обработку исключений или нет. Но является ли неправильный пароль ИСКЛЮЧИТЕЛЬНОЙ ситуацией? Сколько раз из ста пользователь ошибается при наборе пароля?
Согласен с тем, что точных правил нет, но и не пользоваться возможностями try/catch, когда они очень хорошо ложаться как то тоже не хорошо
B>Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?
Вчерась Вообще если циклы сложные, то он часто удобен бывает
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, Borisman.
А>Хотелось бы отметить, что у передачи объекта-исключения указателем есть и определенные преимущества (не умаляя недостатков ).
А>Например, при передаче значением выполнить что-то вроде InnerException в .Net (т. е. обернуть одно исключение другим, так что исходное исключение становится причиной исключения на более высоком уровне абстракции) можно, но становится некрасиво.
Мои 5 копеек не в пользу исключений бросаемых по указателю:
Если исключение пересекает границы модулей то непонятно как его надо удалять(либо накладывать жесткие ограничения линковку CRT к модулям).
sev>>А ниже показано, как это решение работает "в жизни". sev>>void ThrowingFunc() sev>>{ sev>>throw CSystemExceptionPtr(new CSystemException(E_FAIL)); sev>>} sev>>Самое главное, что код абсолютно безопасен (я так думаю во всяком случае sev>> > А я не понял, а что будет если при вызове ThrowingFunc new выкинет скажем > std::bad_alloc? Все-таки не нравится мне эта идея с динамическим выделением > памяти при генерации исключения.
Ну как что будет, полетит std::bad_alloc, вестимо
Только чем это плохо?
> Проще надо ИМХО, конечно.
[...skipped...] > При этом, конечно, придется делать такой typedef для inherited (базового > исключения) в определении исключения, но тогда можно избавиться от того > "typedef'а в минусах".
В точности так я и сделал сначала
Но это замена одного минуса на другой: нужно не забывать делать этот самый
typedef внутри каждого нового класса. Причём, если забыть, то всё будет
"работать".
А вот если нынешний typedef забыть, то станет невозможно написать
соответствующий catch
Да, кстати, тогда придётся в catch писать вот такие монструозные конструкции:
Здравствуйте, sev, Вы писали:
sev>Ну как что будет, полетит std::bad_alloc, вестимо sev>Только чем это плохо?
Например тем, что ловить ты будешь СExceptionPtr (тем более, что в заголовочном файле у тебя уже есть COutOfMemoryExeption). А выкинется std::bad_alloc (особенно в случае COutOfMemoryExeption), которая останется неперехваченной. Или я чего-то не понял?
>> Проще надо ИМХО, конечно.
sev>Предлагайте варианты...
Ну мне такая штука была нужна, просто для записи более подробной отладочной информации о том, что же сбойнуло. Ну так каждое исключение на своем уровне просто писало в лог, то что знало и все — дальше новое throw, если нужно, и перехват в верху . Мне было вполне достаточно. А если речь идет о клиенте, а не о серваке, то юзеру вполне достаточно сказать "облом тебе", а не пугать кучей непонятной информации. Так что получалось дешего и сердито. Просто для того, чтобы что-то предлагать надо бы выснянить, я для чего конкретно такой огород городим.
sev>С уважением, sev>Евгений Суходолин sev>http://www.demoforge.com/
sev>>Ну как что будет, полетит std::bad_alloc, вестимо sev>>Только чем это плохо? > Например тем, что ловить ты будешь СExceptionPtr (тем более, что в > заголовочном файле у тебя уже есть COutOfMemoryExeption).
COutOfMemoryExeption был дан скорее для примера, не нужно
рассматривать его как замену std::bad_alloc, он для этого не задумывался
> А выкинется std::bad_alloc (особенно в случае COutOfMemoryExeption), которая > останется неперехваченной. Или я чего-то не понял?
std::bad_alloc останется неперехваченным тем кодом, который ожидал
увидеть потомка CException. Что вполне логично. Разумеется, что где-то
в программе должен быть и обработчик std::bad_alloc А также всех прочих,
порождённых от std::exception. Т.е. я не претендую на замену std::exception
своими исключениями. Мои исключения _логические_, в то время как
std::bad_alloc сугубо системное.
Как вариант, все не-CException могут ловиться через catch(...) и транслироваться
в CException-derived исключения. Ссыка на оригинальный способ это сделать
здесь уже пробегала.
>>> Проще надо ИМХО, конечно. sev>>Предлагайте варианты... > Ну мне такая штука была нужна, просто для записи более подробной отладочной > информации о том, что же сбойнуло. Ну так каждое исключение на своем уровне > просто писало в лог, то что знало и все — дальше новое throw, если нужно, и > перехват в верху . Мне было вполне достаточно. А если речь идет о клиенте, а > не о серваке, то юзеру вполне достаточно сказать "облом тебе", а не пугать > кучей непонятной информации. Так что получалось дешего и сердито. Просто для > того, чтобы что-то предлагать надо бы выснянить, я для чего конкретно такой > огород городим.
Тут могут быть разные идеи, но я исходил из того, что _мне нужно_ иметь
список вложенных исключений a-la .NET. Я не вижу достойной альтернативы
сделать это без использования динамической памяти.
Здравствуйте, Borisman, Вы писали:
A>>Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.
B>То, что для одного ошибка — для другого — проза жизни . Это смотря с какой стороны посмотреть. Я лишь говорю о том, что нет единого правила, говорящего о том, следует применять обработку исключений или нет. Но является ли неправильный пароль ИСКЛЮЧИТЕЛЬНОЙ ситуацией? Сколько раз из ста пользователь ошибается при наборе пароля?
Эээ... ты случайно исключение (как особая ситуация) и исключительно редко не путаешь?
Вполне себе исключительная. Как пример: запросил пароль, пробуем расшифровать + исключение в случае неудачи.
Чем тут плохо исключение? Ресурсов больше надо? Да явных проверок будет не меньше. Да и куда тут торопится, как можно быстрее еще раз пароль спросить?
К тому же исключения вводят единообразие в обработке ошибок, что кстати очень полезно. И чем больше проект, тем полезнее.
B>Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?
Хм... А ты функции только с одним аргументом используешь?
Здравствуйте, Areex, Вы писали:
B>>Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ? A>Хм... А ты функции только с одним аргументом используешь?
Запятая в списке параметров ф-ции — это не "оператор ,".
Вот если сделать так:
f((x, y, z));
то здесь будет "оператор ,", но и смысл у выражения будет абсолютно другой (ф-ция с одним аргументом).
Здравствуйте, GvozdodeR, Вы писали:
B>>1) throw CMyException(...) B>>2) throw new CMyException(...) GR>В пользу второго способа, imho, есть только один разумный довод (производительность я не считаю, ибо ей с исключениями не по пути), с которым я буквально сегодня столкнулся. Если исключение выбрасывается с помощью способа №1 из .dll, а ловится, к примеру, в .exe, причем при раскрутке стека та самая .dll выгружается, то произойдет Access Violation в catch-блоке.
Здравствуйте, GvozdodeR, Вы писали:
B>> 1) throw CMyException(...) B>> 2) throw new CMyException(...)
G> Если исключение выбрасывается с помощью способа №1 из .dll, а ловится, G> к примеру, в .exe, причем при раскрутке стека та самая .dll выгружается, G> то произойдет Access Violation в catch-блоке.
А при способе (2) этот же Access Violation произойдет в момент delete,
если класс CMyException определен в выгруженной DLL. Что наталкивает
на мысли, что выгружать DLL в этот момент -- не самая хорошая мысль
Posted via RSDN NNTP Server 1.4.6 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>А при способе (2) этот же Access Violation произойдет в момент delete, ПК>если класс CMyException определен в выгруженной DLL. Что наталкивает ПК>на мысли, что выгружать DLL в этот момент -- не самая хорошая мысль
Но только если класс CMyException определен в той самой .dll!!!
При необходимости выгрузки .dll в деструкторе, в случае раскрутки стека выбросом исключения, можно отложить выгрузку .dll на atexit. С другой стороны, если исключение фатально, то зачем удалять экземпляр объекта-исключения, ведь защищенная ОС сама вычистит память после завершения приложения. И еще: если, допустим, наш CMyException наследован от std::exception, то в нем, вероятно, переопределен метод what(), тогда выгружать .dll вообще невозможно, т. к. вместе с ней выгрузится и код метода what().
Здравствуйте, Borisman, Вы писали:
B>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>1) throw CMyException(...) B>2) throw new CMyException(...)
Если пишется MFC приложение и CMyException унаследован от CException, то лучше следовать второму методу, потому что в MFC так делается сплошь и рядом. Тогда один catch блок типа:
catch( Exception * e ) { ... e->Delete(); }
будет работать и для твоих и для MFC исключений.
Почему так сделано в MFC — это уже другой вопрос. Вероятно, по историческим причинам.
Здравствуйте, Borisman, Вы писали:
B>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>1) throw CMyException(...) B>2) throw new CMyException(...)
B> B>И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
B>try { B> ... B> throw new CMyException(...); B>} catch(CMyException *exc) B>{ B> ... B> delete exc; // Ну или там в большинстве случаев — exc->Delete(), которая вызывает delete this; B>}
С точки зрения удобства и надёжности, лучше не использовать new. Но в этом случае нужно помнить что
в catch нужно ловить не CMyException ex, а CMyException & ex (а лучше вообще const CMyException & ex), в противном случае каждый раз будет вызываться конструктор копирования, что может в лучшем случае вызвать замедление работы, а в худшем (если конструктор копирования не описан, а нужен) вообще может привести к краху программы.
Что жа до передачи указателя, то это, на мой взгляд, слишком большой геморрой с потенциальной возможностью налететь потом на memory leaks
Здравствуйте, Borisman, Вы писали:
A>>Нет, ошибка это ошибка. Функция же может только вернуть результат, который можно трактовать как ошибку.
B>То, что для одного ошибка — для другого — проза жизни . Это смотря с какой стороны посмотреть.
Ошибка — проза жизни разве что для неудачника...
B>Я лишь говорю о том, что нет единого правила, говорящего о том, следует применять обработку исключений или нет. Но является ли неправильный пароль ИСКЛЮЧИТЕЛЬНОЙ ситуацией? Сколько раз из ста пользователь ошибается при наборе пароля?
Если применять, то уже применять — нет, так не применять вообще. ИМХО
A>>Ты не аргументировал, почему не следует. Обработка ошибок прямое назначение исключений. Этак половину c++ можно не использовать, мало ли у кого какой здравый смысл.
B>Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?
Да, не следует. Но применять разные средства обработки подобных ситуаций еще более не следует.
for (i = 0, j = 0; i < first && j < second; i++, j++)
{
cout << i << " -> " << j << endl;
}
А вообще-то "," в циклах очень часто встречается в сложных итераторах...
Здравствуйте, The Lex, Вы писали:
T>>Вот так конструктор копий вызывается T>>
T>>CEx ex( 10 );
T>>throw ex;
T>>
T>>а вот так — нет T>>
T>>throw CEx( 10 );
T>>
T>>и стандарт это разрешает (пункт не помню)
Пункт 15.3/18 (приведен ниже)
TL> Так ведь второй случай — это не констуктор копирования TL>
TL>CEx(const CEx& )
TL>
а просто конструктор TL>
TL>CEx(int )
TL>
TL> учите матчасть...
То, что это — не конструктор копирования — верно. Но верно так же и то, что вслед за вызовом этого конструктором вполне может последовать вызов конструктора копирования для создания копии выбрасываемого объекта:
15.1/3A throw-expression initializes a temporary object <...skip...>
The temporary is used to initialize the variable named in the matching handler (15.3).
Временный объект может создаваться или не создаваться. И конструктор копирования может либо вызываться, либо не вызываться соответственно:
15.3/18If the use of a temporary object can be eliminated without changing the meaning of the program except for
execution of constructors and destructors associated with the use of the temporary object, then the optional
name can be bound directly to the temporary object specified in a throw-expression causing the handler to
be executed. The copy constructor and destructor associated with the object shall be accessible even when
the temporary object is eliminated.