Здравствуйте, Borisman, Вы писали:
B>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>1) throw CMyException(...) B>2) throw new CMyException(...)
Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому интересно, насколько быстро моя программа может сбоить???
Никогда не используй try-catch для программирования логики. А если ты это сделал, то не ломай голову над производительностью программы. Одно другое исключает. Вот так.
Здравствуйте, 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.
B>Вообще-то я довольно много где видал именно второй способ выброса исключений. В первый раз увидел его в здоровенном финансовом приложении HPR. Очень меня это сбило с панталыку — глупые программеры того проекта утверждали, что если выбрасывать стековый объект, то при раскрутке стека он того....удалится в общем.
Чем тяжелее бред, тем труднее на него реагировать...
А вообще, есть книжки Страуструпа (естественно) и Мейерса, где все расписано. Надо просто достать и прочитать внимательно. А резюме такое:
1) выбрасывается ТОЛЬКО стековый объет (НИ В КОЕМ СЛУЧАЕ НЕ УКАЗАТЕЛЬ)
2) перехватывается ССЫЛКА (не значение!).
На вопрос, как нужно выбрасывать исключения, есть два мнения:
1) throw CMyException(...)
2) throw new CMyException(...)
И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
try {
...
throw new CMyException(...);
} catch(CMyException *exc)
{
...
delete exc; // Ну или там в большинстве случаев — exc->Delete(), которая вызывает delete this;
}
Второй подход вроде как хуже из-за дополнительной необходимости удалять исключение. Или это фича?
Здравствуйте, Borisman, Вы писали:
B>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>1) throw CMyException(...) B>2) throw new CMyException(...)
B> B>И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
Угу. Первый чуть медленнее, поскольку выброшенный CMyException будет неявно копироваться при передаче из try-блока в catch.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, Borisman, Вы писали:
B>>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>>1) throw CMyException(...) B>>2) throw new CMyException(...)
B>> B>>И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
ГВ>Угу. Первый чуть медленнее, поскольку выброшенный CMyException будет неявно копироваться при передаче из try-блока в catch.
Не знаю, не проверял, но при втором много потенциальных взможностьей упустить память — пример
try
{
throw new CMyException();
}catch(...)
{
//попасть мы сюда попали, а память освобождать как?
}
Пример несколько надуман, но если нижележащий код кидает в try указатель, а ты об этом не знаешь, то найти этот лик в памяти будет очень проблематично.
Здравствуйте, Геннадий Васильев, Вы писали:
ГВ>Здравствуйте, Borisman, Вы писали:
B>>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>>1) throw CMyException(...) B>>2) throw new CMyException(...)
B>> B>>И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
ГВ>Угу. Первый чуть медленнее, поскольку выброшенный CMyException будет неявно копироваться при передаче из try-блока в catch.
Это если напишешь catch(CMyExeption). А ты так не пиши, ты пиши catch(CMyExeption&) как все нормальные люди.
Вообще первый способ (с new) не просто кривой, а очень кривой. Во первых, почти гарантирована утечка памяти — кто-нибудь либо не поймает, либо не удалит. Например в случае, если исключение попадает в блок catch(...) откуда ты узнаешь, что нужно сделать delete CMyExeption.
Во вторых, страдает столь любимая всеми нами производительность. Операция new — вещь в этом смысле очень дорогая...
Здравствуйте, Borisman, Вы писали:
B>>На вопрос, как нужно выбрасывать исключения, есть два мнения: B>>1) throw CMyException(...) B>>2) throw new CMyException(...)
B>> B>>И то и то работает. Но во втоом случае приходится явно удалять экземпляр исключения в секции catch:
ГВ>Угу. Первый чуть медленнее, поскольку выброшенный CMyException будет неявно копироваться при передаче из try-блока в catch.
Строго говоря, первый способ медленнее на передаче. Второй способ заторомозит при создании объекта-исключения, чем скорее всего сожрёт все "выигрыши". (псхпп grs)
Кстати, второй способ менее безопасен, поскольку new сама по себе может стать источником исключений.
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Здравствуйте, grs, Вы писали:
grs>Это если напишешь catch(CMyExeption). А ты так не пиши, ты пиши catch(CMyExeption&) как все нормальные люди.
Верное замечание! Большое спастбо, чуствуется, что соображает головой человек. Собственно, так и надо было мне первый пример написать, но я решил упростить.
Вот только одна закавыка: я толком не знаю, как раскручивается стек при ислючении, но тут разница между двумя подходами в том, где именно лежит все исключение-объект. В первом случае в стэке лежит он весь, а во втором — только указатель. Интересно, что проще для компилятора — раскрутить стек так, чтоб доступен был указатель (4 байта) или весь объект (много байтов).
Вообще-то я довольно много где видал именно второй способ выброса исключений. В первый раз увидел его в здоровенном финансовом приложении HPR. Очень меня это сбило с панталыку — глупые программеры того проекта утверждали, что если выбрасывать стековый объект, то при раскрутке стека он того....удалится в общем. Но потом, хорошенько подумав, я решил, что вроде-как не должен. Ведь делаем же мы вещи типа throw "приветик!" — и ниче...все корректно...
Еще есть подозрение, что просто не все компиляторы одинаково исключения обрабатывают (вспомним, что в старых версиях компиляторов С++ они вообще не везде были) и где-то как-то по разному работает выбрасывание стековых объектов. А вот динамические объекты типа работают....
Re[4]: Зачем надо throw new CMyException
От:
Аноним
Дата:
11.03.03 12:32
Оценка:
Здравствуйте, Borisman.
Хотелось бы отметить, что у передачи объекта-исключения указателем есть и определенные преимущества (не умаляя недостатков ).
Например, при передаче значением выполнить что-то вроде InnerException в .Net (т. е. обернуть одно исключение другим, так что исходное исключение становится причиной исключения на более высоком уровне абстракции) можно, но становится некрасиво.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Ведмедь, Вы писали:
В>>Видимо имелос ввиде то что try/catch вызывает некоторый провал в производительности.
А>... и, что еще более важно, в прозрачности и сопровождаемости кода.
Ну не всегда. в некоторых случаях IMHO прозрачней как раз через try/catch ( например, выход из N вложенных циклов )
К>Если ты будешь передавать исключение дальше, да еще, завернув его в другое, то, возможно, второй подход будет лучше. Но я так никогда не делаю. И вообще не понимаю, суть обсуждения производительности блоков try-catch. Кому интересно, насколько быстро моя программа может сбоить???
Кстати не совсем верно. В данном вопросе да, производительность значения не имеет. Я тоже считаю, что если произошло исключение, то обработать его в 90 процентов случае время есть и можно уже не торопиться Но факт в том что try/catch не в лучшей сторону влияет на производительность, не зависимо от того, происходит или нет исключение. А вот это не приятно.
Да пребудет с тобой Великий Джа
Re[6]: Re[2]: Зачем надо throw new CMyException
От:
Аноним
Дата:
11.03.03 16:41
Оценка:
Здравствуйте, Ведмедь, Вы писали:
В>>>Видимо имелос ввиде то что try/catch вызывает некоторый провал в производительности.
А>>... и, что еще более важно, в прозрачности и сопровождаемости кода.
В>Ну не всегда. в некоторых случаях IMHO прозрачней как раз через try/catch ( например, выход из N вложенных циклов )
Вы будете смеяться, но goto еще никто не отменял. А избежание goto — не самоцель.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Ведмедь, Вы писали:
А>Вы будете смеяться, но goto еще никто не отменял. А избежание goto — не самоцель.
goto не сделаешь из вложенной функции, кроме того в зависимости от типа кидаемого исклюения обработка может происходить в разных местах.
пример —
void CheckSome( int a )
{
if( a = 0 )
throw (int)1;//завершили
}
void SomeFunc()
{
try
{
for(...)
...
for(..)
{
CheckSome( a );
}
}catch( int i )
{
printf( "some var = %d", i );
}
}
Да пребудет с тобой Великий Джа
Re[8]: Re[2]: Зачем надо throw new CMyException
От:
Аноним
Дата:
11.03.03 17:10
Оценка:
Здравствуйте, Ведмедь, Вы писали:
В>goto не сделаешь из вложенной функции, кроме того в зависимости от типа кидаемого исклюения обработка может происходить в разных местах. В>пример —
В>
В>void CheckSome( int a )
В>{
В> if( a = 0 )
В> throw (int)1;//завершили
В>}
В>void SomeFunc()
В>{
В>try
В>{
В> for(...)
В> ...
В> for(..)
В> {
В> CheckSome( a );
В> }
В>}catch( int i )
В>{
В> printf( "some var = %d", i );
В>}
В>}
В>
Ой, какой бандитизм! Ничего не ясно.
Может, так?
bool CheckSome( int a )
{
return a = 0;
}
void SomeFunc()
{
for(...)
...
for(..)
{
if (CheckSome( a )) goto found;
}
found:
printf( "some var = %d", i );
}
он того....удалится в общем.
grs>Чем тяжелее бред, тем труднее на него реагировать...
grs>А вообще, есть книжки Страуструпа (естественно) и Мейерса, где все расписано. Надо просто достать и прочитать внимательно. А резюме такое: grs>1) выбрасывается ТОЛЬКО стековый объет (НИ В КОЕМ СЛУЧАЕ НЕ УКАЗАТЕЛЬ)
Выбрасывается всегда копия.
class EEE
{
private:
EEE(const EEE&); // закоментировать, чтобы откомпилилось
};
void f()
{
throw EEE();
}
int _tmain(int argc, _TCHAR* argv[])
{
f();
return 0;
}
grs>2) перехватывается ССЫЛКА (не значение!).
только если использовать синтаксис по ссылке (catch(some&)).
Приветствую!
> На вопрос, как нужно выбрасывать исключения, есть два мнения: > 1) throw CMyException(...) > 2) throw new CMyException(...)
[...skipped...] > Второй подход вроде как хуже из-за дополнительной необходимости удалять > исключение. Или это фича? > Бойцы, у кого какое мнение?
Замечательный вопрос. Я буквально на днях всерьёз озаботился этой проблемой.
После некоторых раздумий я реализовал решение, коим весьма теперь доволен.
Все проблемы с автоматическим удалением исключения в нём решены, а также
добавлена возможность "вложенности" исключений в стиле .NET.
void UserFunc()
{
try
{
LibraryFunc();
}
catch (CExceptionPtr& e)
{
CException* pe = e.get();
while (pe)
{
printf("%s", pe->GetMessage()); // выводим стек ошибок
pe = pe->GetInnerException();
}
}
}
Самое главное, что код абсолютно безопасен (я так думаю во всяком случае
в плане возможных утечек памяти. Т.е. не напрягаясь делаем new, и не вспоминаем
о delete Что принципиально отличает это решение от того, что есть в MFC.
Также мы можем свободно передавать исключения из потока в поток, т.е. для этого
их не нужно клонировать
Теперь минусы: единственным отрицательным моментом, на мой взгляд, является
то, что для каждого нового класса исключений нужно не забыть сделать один
дополнительный typedef, благодаря которому появляется возможность пользоваться
приведением к базовому классу в catch. В вышеприведённом коде видно, что мы
ловим более абстрактное исключение, чем выбросили.
Здравствуйте, 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++ можно не использовать, мало ли у кого какой здравый смысл.
Не следует использовать средства языка только потому, что они есть. Если нет смысла в применении половины С++ — значит, так тому и быть. Когда Вы в последний раз применяли оператор , (запятая) ?
Здравствуйте, 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;
}
А вообще-то "," в циклах очень часто встречается в сложных итераторах...