Comments 159
«Небольшой практический опыт» — это сколько?
+1
Чуть больше 15 лет.
+15
думал, что сперва в статье вы запоёте очередную песню про «полезность» кодов ошибок, но прочитав увидел мнение с котором сам согласен. В ООП нужно пользоваться только исключениями. Кстати, мне сказали, что исключения тормозят систему… я решил проверить скорость обработки 10000 подряд вызванных исключений vs кодов ошибок. конечно коды работают раз в 100 быстрее(на .net пробовал), но вот показатель 0,0001с vs 0,01с для 10000 брошенных подряд случайно сгенерированных исключений — это весомый аргумент за использование последних без оглядки на «упавшую» производительность.
+7
Я в целом придерживаюсь мнения, что оптимизировать стоит после того как сделан прототип и профайлер определил место где у нас тормозит. Если тормозит. Большая же часть кода который мы пишем не очень нуждается в оптимизациях и даже переживает сложность O(n**2) :).
+5
>> Кстати, мне сказали, что исключения тормозят систему…
очередная экономия на спичках!
очередная экономия на спичках!
+2
Вы не правы
-4
Исключения в С++ не бесплатны. В случае, когда они не выбрасываются разница в производительности незаметна, но если они будут выбрасываться очень часто это может сказаться на производительности. Поэтому есть хорошее правило — исключения должны выбрасываться в исключительных случаях.
Проверено на опыте кстати. Xerces-C использует исключения для сигнализации об ошибках, так вот у нас парсились немного невалидные xmlки. Xerces-C парсер бросал исключение и сам же его ловил на пару фреймов ниже по стеку. Парсились они очень часто и после того, как xmlку подправили, кусок стал выполняться в разы быстрее, даже на глаз было заметно, без всяких профайлеров.
Проверено на опыте кстати. Xerces-C использует исключения для сигнализации об ошибках, так вот у нас парсились немного невалидные xmlки. Xerces-C парсер бросал исключение и сам же его ловил на пару фреймов ниже по стеку. Парсились они очень часто и после того, как xmlку подправили, кусок стал выполняться в разы быстрее, даже на глаз было заметно, без всяких профайлеров.
+3
Может этот парсер сам по себе кучеряво написан? Вы меня уж извените но что бы тормозить при парсинге xml файлов, это надо постараться…
0
у нас такое было в нашем же коде — функция возвращала точку пересечения двух прямых. Исключение кидалось если они не пересекались. Код вызывался для прорисовки разных объектов на экране — и пока там были исключения тормозил все дело так, что водить мышкой было неприятно ( ощутимые задержки ). Коды возврата полностью убрали тормоза.
С тех пор очень настороженно отношусь к исключеням.
С тех пор очень настороженно отношусь к исключеням.
+2
Так про это же и говорят — начинайте оптимизировать, если видите проблему, а пока проблемы нет пользуйтесь исключениями. Сколько можно одно и тоже-то повторять…
+2
Вы сделали выброс исключения частью логики своей программы. Они же должны обрабатывать только ошибки (исключительные ситуации).
0
Удивительно, но наличие в C#-функции блока try/catch замедлило скорость работы этой функции в 4 раза — притом, что сам блок находился в неисполнявшейся части кода. Я сравнивал функции
Передавались ненулевые значения параметра.
Но главный недостаток использования исключений в «штатном» выполнении программы — они очень мешают отладке. Сейчас мне отладчик сразу показывает, где объект оказался null, а когда в программе использовались исключения, то отладчик на них все время спотыкался.
int f1(int k){ if(k==0){ try{ File.Delete("tmp.tmp"); }catch{} } return 2*k; } int f2(int k){ if(k==0) File.Delete("tmp.tmp"); return 2*k; }
Передавались ненулевые значения параметра.
Но главный недостаток использования исключений в «штатном» выполнении программы — они очень мешают отладке. Сейчас мне отладчик сразу показывает, где объект оказался null, а когда в программе использовались исключения, то отладчик на них все время спотыкался.
+1
Действительно, а как можно прямым способом обнаружить источник исключения? То есть без построения предположений о том откуда оно могло взяться, а именно точно знать откуда. Особенно, если мы его не сами выбрасываем и точку останова нельзя поставить.
Что-то в дебаггере Visual Studio 2010 Express я этого не находил.
Что-то в дебаггере Visual Studio 2010 Express я этого не находил.
0
> В ООП нужно пользоваться только исключениями.
Не всегда и не везде:
today.java.net/article/2006/04/04/exception-handling-antipatterns#antipatterns
stackoverflow.com/questions/2870701/risking-the-exception-anti-pattern-with-some-modifications
www.rockstarprogrammer.org/post/2007/jun/09/java-exception-antipatterns/
etc
> Кстати, мне сказали, что исключения тормозят систему…
В целом, они отрабатывают медленнее. Но, стоит ли гнаться за этими миллисекундами, если bottleneck в другом месте?
Не всегда и не везде:
today.java.net/article/2006/04/04/exception-handling-antipatterns#antipatterns
stackoverflow.com/questions/2870701/risking-the-exception-anti-pattern-with-some-modifications
www.rockstarprogrammer.org/post/2007/jun/09/java-exception-antipatterns/
etc
> Кстати, мне сказали, что исключения тормозят систему…
В целом, они отрабатывают медленнее. Но, стоит ли гнаться за этими миллисекундами, если bottleneck в другом месте?
+3
Не всегда и не везде:
today.java.net/article/2006/04/04/exception-handling-antipatterns#antipatterns
stackoverflow.com/questions/2870701/risking-the-exception-anti-pattern-with-some-modifications
www.rockstarprogrammer.org/post/2007/jun/09/java-exception-antipatterns/
etc
По ссылкам 1 и 3 — описание, что не стоит делать, при использовании исключений, а не примеры, почему исключениями иногда не стоит пользоваться. 2 — оно как бы не совсем про ООП.
0
> По ссылкам 1 и 3 — описание, что не стоит делать, при использовании исключений, а не примеры, почему исключениями иногда не стоит пользоваться.
Там есть замечательные примеры с пробросом исключений.
> 2 — оно как бы не совсем про ООП.
Оно легко адаптируется к использованию с ООП. Не нашел толкового примера на C++, но думаю мысль и из этого понятна.
Там есть замечательные примеры с пробросом исключений.
> 2 — оно как бы не совсем про ООП.
Оно легко адаптируется к использованию с ООП. Не нашел толкового примера на C++, но думаю мысль и из этого понятна.
0
Я к тому веду — что не стоит пихать исключения везде, часто можно (и правильнее) обойтись и без них.
0
Есть очень простой универсальный критерий, выводящийся из самого смысла понятия «исключительная ситуация» — используйте исключения только для сбоев.
0
Исключениями удобно делать что-то такое:
public Person authPerson(int id) throws NoSuchPersonException {
Person person = new Person(id); // бросаем NoSuchPersonException если пользователя с таким id нет
person.applySomeRulesOrSomething();
return person.auth();
}
Возвращаем объект Person если все ОК, и null если не прошла авторизация (в т.ч. у пользователя не хватает пермишенов).
Врядли это можно назвать сбоем, а вот исключительной ситуацией — вполне.
public Person authPerson(int id) throws NoSuchPersonException {
Person person = new Person(id); // бросаем NoSuchPersonException если пользователя с таким id нет
person.applySomeRulesOrSomething();
return person.auth();
}
Возвращаем объект Person если все ОК, и null если не прошла авторизация (в т.ч. у пользователя не хватает пермишенов).
Врядли это можно назвать сбоем, а вот исключительной ситуацией — вполне.
0
По мне — это именно сбой. По крайней мере на том уровне, где идет проверка всего необходимого для авторизации. Так как если у пользователя нет права на авторизацию — то в идеале и доступ к попыткам авторизоваться должен быть недоступен.
Общий принцип: если нажатие на кнопку ведет к посылу на предмет нехватки прав, то кнопка должна быть неактивной.
Общий принцип: если нажатие на кнопку ведет к посылу на предмет нехватки прав, то кнопка должна быть неактивной.
0
Имхо, сбой — это нештатная ситуация. Деление на ноль, ошибка преобразования строки в число, ошибка соединения клиентской части с серверной, итд.
В данном случае отбой при недостатке прав — это ожидаемая, штатная ситуация.
В данном случае отбой при недостатке прав — это ожидаемая, штатная ситуация.
0
Деление на ноль, ошибка преобразования строки в число — вполне себе штатные ситуации при разборе пользовательского ввода и использовании его для вычислений.
Отбой при недостатке прав как штатную ситуацию я бы посчитал недостатком проектирования — возможностью послать заведомо невыполнимую команду. Например, в вашем случае добавил бы функцию безопасной проверки наличия права на авторизацию, по которой давал бы пользователю доступ собственно к команде «Авторизовать». Или счел бы сбоем организационного уровня — а почему это вдруг пользоваетль с недостаточными правами хочет странного?
Отбой при недостатке прав как штатную ситуацию я бы посчитал недостатком проектирования — возможностью послать заведомо невыполнимую команду. Например, в вашем случае добавил бы функцию безопасной проверки наличия права на авторизацию, по которой давал бы пользователю доступ собственно к команде «Авторизовать». Или счел бы сбоем организационного уровня — а почему это вдруг пользоваетль с недостаточными правами хочет странного?
0
> Например, в вашем случае добавил бы функцию безопасной проверки наличия права на авторизацию, по которой давал бы пользователю доступ собственно к команде «Авторизовать».
Следует заметить, что во-первых, пример был взят исключительно, что называется, с потолка.
И во-вторых — мысль была такая —
— Есть id некоего пользователя.
— Проверить, имеет ли этот пользователь доступ к неким «продвинутым» фичам.
— Если имеет — авторизовываем в вызвавшей подсистеме.
Авторизация — это собственно и есть способ получить данные пользователя, проверить их валидность и определить что пользователь может а что не может.
Ну и в третьих — это имеет весьма косвенное отношение к обсуждаемому вопросу.
Собственно, тут можно обойтись и без исключений, с обычными кодами возврата. Но это, на мой взгляд не так удобно.
Следует заметить, что во-первых, пример был взят исключительно, что называется, с потолка.
И во-вторых — мысль была такая —
— Есть id некоего пользователя.
— Проверить, имеет ли этот пользователь доступ к неким «продвинутым» фичам.
— Если имеет — авторизовываем в вызвавшей подсистеме.
Авторизация — это собственно и есть способ получить данные пользователя, проверить их валидность и определить что пользователь может а что не может.
Ну и в третьих — это имеет весьма косвенное отношение к обсуждаемому вопросу.
Собственно, тут можно обойтись и без исключений, с обычными кодами возврата. Но это, на мой взгляд не так удобно.
0
В динамически-типизированом ООП код возврата типа «никакой объект» более, чем вполне уместен (если невозможность вернуть объект — «ожидаемая» ошибка в логике той функции, котрая возвращает null)
+2
Да, про это я тоже написал :).
0
По моему опыту использовать в таких случаях null — явный антипаттерн, приводящий к гекатомбам лишних проверок с одной стороны и ошибкам периорда выполнения в местах, далеких от ошибок кода — с другой. Можешь не вернуть результат — пиши if (TryGetResult(Result))… — и всем все будет ясно уже по сингнатуре. Не зря Хоар само введение null считал одним из самых дорогих решений в IT.
0
> 0,01с
Вообще-то 10мс это дофига, даже для .net. Вы что-то неправильно намерили.
Вообще-то 10мс это дофига, даже для .net. Вы что-то неправильно намерили.
-1
А теперь представьте что штатное боевое время работы вашей программы — суток так 3-4.
0
Если 10000 исключений вызываются за 0,01 с, то представьте сколько исключений может вызваться за суток так 3-4.
Что если по большей части программа будет вызывать исключения и обрабатывать их (будет абсолютно правильно с точки зрения логики) вместо того чтобы возвращать коды возврата. Тогда время работы 3-4 дня превратится в 4+ дня.
Думаю время работы не совсем правильный аргумент.
Конечно это не относится к программам которые работают 3-4 дня из-за того, что по минуте ждут ответа от сервиса.
Что если по большей части программа будет вызывать исключения и обрабатывать их (будет абсолютно правильно с точки зрения логики) вместо того чтобы возвращать коды возврата. Тогда время работы 3-4 дня превратится в 4+ дня.
Думаю время работы не совсем правильный аргумент.
Конечно это не относится к программам которые работают 3-4 дня из-за того, что по минуте ждут ответа от сервиса.
0
Перепроверил измерения, у меня получилось 20-25мс на выброс и отлов 1000 исключений.
ИМХО конечно, но это слишком много. Это ведь не просто 25 мс, а полная загрузка процессора(если быть точным то 1го ядра) и в этом время ничего не происходит.
То есть если взять средненагруженный сервер где сидит хотя бы сотня пользователей одновременно и представить что программисты кидают исключения в любой нестандартной ситуации — на мой взгляд пользоваться этим сервисом будет попросту невозможно.
:) Первый пост
ИМХО конечно, но это слишком много. Это ведь не просто 25 мс, а полная загрузка процессора(если быть точным то 1го ядра) и в этом время ничего не происходит.
То есть если взять средненагруженный сервер где сидит хотя бы сотня пользователей одновременно и представить что программисты кидают исключения в любой нестандартной ситуации — на мой взгляд пользоваться этим сервисом будет попросту невозможно.
:) Первый пост
+1
А откуда у вас 1000 исключений при штатной работе сервиса?
0
А при слегка нештатной значит можно уйти в бесконечный вис? :)
А если серьёзно — разговор в топике вроде был о полном отказе от кодов возврата и как следствие от гибридной схемы тоже. Значит многие операции будут возвращать не null/false — а простые исключения. Я уверен что при переходе на схему чистых исключений, оных будут сотни и тысячи.
Как частный пример приведу Dictionary.TryGetValue() или Int32.TryParse() которые возвращают коды ошибок(bool). Для них есть варианты с исключениями — для Dictionary — индексатор, а для Int32 — Parse(). Вот вторые и породят тысячи исключений.
Если мне не изменяет память микрософт в гайдах для .NET рекомендует использовать исключения только в ИСКЛЮЧИТЕЛЬНЫХ ситуациях(по первой просьбе найду пруфлинк).
Я сам дотнетчик, поэтому говорю только за него.
А если серьёзно — разговор в топике вроде был о полном отказе от кодов возврата и как следствие от гибридной схемы тоже. Значит многие операции будут возвращать не null/false — а простые исключения. Я уверен что при переходе на схему чистых исключений, оных будут сотни и тысячи.
Как частный пример приведу Dictionary.TryGetValue() или Int32.TryParse() которые возвращают коды ошибок(bool). Для них есть варианты с исключениями — для Dictionary — индексатор, а для Int32 — Parse(). Вот вторые и породят тысячи исключений.
Если мне не изменяет память микрософт в гайдах для .NET рекомендует использовать исключения только в ИСКЛЮЧИТЕЛЬНЫХ ситуациях(по первой просьбе найду пруфлинк).
Я сам дотнетчик, поэтому говорю только за него.
+1
А что значит «слегка»?
Полный отказ от кодов возврата <> полный отказ от кодов ошибок.
Возвращать false ИМХО можно и нужно, если это часть штатной логики работы программы и надо бросать исключение, если это сбой.
Поэтому GetValue и TryGetValue нисколько друг другу не противоречат, а только дополняют: для второй функции неудачный разбор сбоем не является!
Полный отказ от кодов возврата <> полный отказ от кодов ошибок.
Возвращать false ИМХО можно и нужно, если это часть штатной логики работы программы и надо бросать исключение, если это сбой.
Поэтому GetValue и TryGetValue нисколько друг другу не противоречат, а только дополняют: для второй функции неудачный разбор сбоем не является!
0
Вы зря дискутируете со мной насчёт кодов возврата :) Я ведь с вами согласен что исключения нужны кидать в ситуации сбоя.
Это идея топикстартера о том что приложение не должно пользоваться кодами возврата, а только использовать исключения для ситуаций когда метод не смог выполнить задачу.
Я так понимаю что случий с Int32.Parse как раз то чего желает топикстартер. Я же никогда им не пользуюсь, и предпочитаю TryParse.
Это идея топикстартера о том что приложение не должно пользоваться кодами возврата, а только использовать исключения для ситуаций когда метод не смог выполнить задачу.
Я так понимаю что случий с Int32.Parse как раз то чего желает топикстартер. Я же никогда им не пользуюсь, и предпочитаю TryParse.
+1
я думаю, что вы не правильно поняли топикстаретра.
Коды возврата — это не тоже самое, что возврат null или true/false.
Пример: если строка в подстроке не найдена, то это false, а не исключение.
А если в на вход функции пришёл null вместо строки которую будут искать, то это исключение, а не null или false. И уже тем более не "-1"
Пример2: из базы получили ВСЕ объекты из списка ID. ( Select * from...where id IN {1,2,3,4} )
Так вот, если метод не нашёл объекты, то он возвращает пустой список. А если он нашёл только 3 объекта из 4, то он возвращает исключение, т.к. это не штатная ситуация — в базе нет объекта, id которого у вас каким-то образом появился. Для нормальной бизнес-логики это не нормально. Если бы тоже самое было не по ID по stateId или Amount, то исключение кидать не стоит, а просто вернуть 3 объекта.
Коды возврата — это не тоже самое, что возврат null или true/false.
Пример: если строка в подстроке не найдена, то это false, а не исключение.
А если в на вход функции пришёл null вместо строки которую будут искать, то это исключение, а не null или false. И уже тем более не "-1"
Пример2: из базы получили ВСЕ объекты из списка ID. ( Select * from...where id IN {1,2,3,4} )
Так вот, если метод не нашёл объекты, то он возвращает пустой список. А если он нашёл только 3 объекта из 4, то он возвращает исключение, т.к. это не штатная ситуация — в базе нет объекта, id которого у вас каким-то образом появился. Для нормальной бизнес-логики это не нормально. Если бы тоже самое было не по ID по stateId или Amount, то исключение кидать не стоит, а просто вернуть 3 объекта.
+1
Полагаю, что TryParse ничуть не противоречит идее топикстартера. Как видно из самой сигнатуры, задача метода попробовать разобрать строку и вернуть явный флаг — ее он успешно и выполняет. Ошибкой неудача разбора здесь не является, а значит, исключений, по идее автора, использовать не нужно. Пример — ошибка компиляции не является в общем случае ошибкой компилятора.
0
Не в тему:
Напишите пожалуйста пост про константность, ту что делится на «физическую» и «логическую». Потому что это скользкий и очень важный момент при разработке на С++. Только прошу написать более развернутый пост, чем на rsdn.ru
Напишите пожалуйста пост про константность, ту что делится на «физическую» и «логическую». Потому что это скользкий и очень важный момент при разработке на С++. Только прошу написать более развернутый пост, чем на rsdn.ru
+2
Это чудовищно сложный вопрос :(.
Но за идею спасибо. У меня есть ряд наработок, как раз недавно обнаружил что на примере implicitly shared контейнеров в Qt достаточно просто показать зачем на самом деле нужно ключевое слово const.
Я попробую написать, но сами понимаете — это далеко не на один день развлечение. Эта статья, например, писалась три дня О_О.
Но за идею спасибо. У меня есть ряд наработок, как раз недавно обнаружил что на примере implicitly shared контейнеров в Qt достаточно просто показать зачем на самом деле нужно ключевое слово const.
Я попробую написать, но сами понимаете — это далеко не на один день развлечение. Эта статья, например, писалась три дня О_О.
0
Первые реализации исключений, особенно в C++, были с фатальными изъянами. Выбрасывание исключения в конструкторе приводило к тому, что деструктор не вызывался.
А что, не в «первых» реализациях деструктор вызывается? И в чем изьян? Что вы собираетесь разрушать?
А что, не в «первых» реализациях деструктор вызывается? И в чем изьян? Что вы собираетесь разрушать?
+6
UFO just landed and posted this here
И что? Пусть генерируются. Что вы собираетесь разрушать? Несконструированный объект в data members которого похозяйничал великий рандом? И разве этот «недостаток» исправили? Или кто-то тут ошибся, или та бабочка, которую я раздавил во время вчерашней экскурсии в прошлое, все таки, на что-то повлияла.
+10
UFO just landed and posted this here
Отвечаю по порядку.
1. Нет, не вызывается. Чего не скажешь о реализациях в других языках, например herbsutter.com/2008/07/25/constructor-exceptions-in-c-c-and-java/
2. Изъян в том, что требует написания большого количества «инфраструктурного» кода, никаки н связанного с логикой приложения а требуемого только на поддержание целостности архитектуры.
3. Если у нас сложный класс, объект которого в конструкторе инициализирует ряд ресурсов — то мы хотим, чтобы в случае исключения в середине инициализации мы могли относительно легко разрушить уже созданное. А в C++ нам приходится вместо деструктора использовать «умные» указатели и RAII — для полей класса деструкторы все же вызываются :).
1. Нет, не вызывается. Чего не скажешь о реализациях в других языках, например herbsutter.com/2008/07/25/constructor-exceptions-in-c-c-and-java/
2. Изъян в том, что требует написания большого количества «инфраструктурного» кода, никаки н связанного с логикой приложения а требуемого только на поддержание целостности архитектуры.
3. Если у нас сложный класс, объект которого в конструкторе инициализирует ряд ресурсов — то мы хотим, чтобы в случае исключения в середине инициализации мы могли относительно легко разрушить уже созданное. А в C++ нам приходится вместо деструктора использовать «умные» указатели и RAII — для полей класса деструкторы все же вызываются :).
+3
Язык такой. Или автоматическая инициализация нулями потребуется, потому что вы не сможете в деструкторе определить что удалять. Но это противоречит идеологии С++ — ты не платишь за то, что не используешь Плюс надо будет удалить из языка ссылочные типы, которые не могут быть неинициализированными. В общем, в С++ очень хорошая для С++ реализация исключений, разве только finally забыли.
+3
Я в целом не спорю, что для своего времени реализация была очень неплохая. Но, как я уже писал выше — не без… хммм… косяков :). Программисту же что надо — ему надо чтобы было удобно. А как это «удобно» пересекается с синтаксисом и концепциями языка для программиста дело десятое. И нет ему дела до того, что исключение во время исключения вызывает логическую неопределенность — у него клиенты на телефоне с программой которая «просто выходит и все».
+1
Не-не, об этом можно говорить только если рассматривать сферические исключения в вакууме. Есть язык, в который интегрировали фичу и притом достаточно хорошо и красиво. Все очень даже неплохо. Вы же не станете наезжать на С++ за то, что он не функциональный или за то, что он не интерпретируемый. Смысл в таких наездах? Ровно так же упреки насчет фатальных изъянов смотрится. В чем фатальность? Проблемы решаются довльно просто: вы уже упомнянули RAII, а семантика игнора — это catch(expectable&){}. На мой взгляд такой винегрет получился как раз из-за втрого пункта — из-за самих разработчиков, которым не объяснили (правда, кто должен был это делать?) и из-за legacy кода.
+1
Убедили. Поменял формулировку на более нейтральную. А catch(expectable&){} работать не будет, потому что разработчикам забыли сказать про то, что нужно выделить expectable :). В Java это сделано, на мой взгляд, удобнее с прописыванием что метод может кидать в каких случаях.
+1
Да, в других языках есть много удобных фишек, которые делают использование исключений и более приятным, и вообще не игнорируемым.
+1
Насчет expectable. Я немного другое имел в виду. Не то, что надо какой-то expectable вводить, врядли при написании функции всегда можно оценить что есть expectable, да и не совсем это правильно смотрится. Просто в вызывающем контексте таким образом можно игнорить некоторые ошибки или группы ошибок с общим предком.
0
Проблема в том, что для C++ это потребует писать немаленькую конструкицю try + catch в каждом таком участке, что собственно мы и видим в большом количестве кода. А в случае с возвращаемым значением мы можем просто ничего не писать (это для примера, я на самом деле не знаю как можно хорошо реализовать подобную семантику).
+1
Есть такое. Я как-то испробовал другой подход — практически бинарный — есть ошибка, нет ошибки. Т.е. функция может либо вернуть что-то, с чем можно дальше работать, либо все пропало. Вы знаете, очень неплохо получилось и вполне себе функицонировало. Но, наверное, не для всех классов задач такое подходит.
0
UFO just landed and posted this here
Тут немного некорректный код :). По феншую должно быть вот так:
Foo() :
data( 0 )
{
// ... развлекаемся
+2
UFO just landed and posted this here
Обнулять примитивные типы в конструкторе — это не усложнение, это базовая практика для C++ :)
+2
UFO just landed and posted this here
А в чем проблема-то? delete на 0 в C++ работает корректно, насколько я помню — ничего не делает.
+2
UFO just landed and posted this here
Да, сначала вызовется у file, затем у data, но проблемы в приведенном коде нет. Если File::File выбросит исключение, то Foo::~Foo вызван не будет, соответственно delete не вызовется с каким-то мусором, если вы об этом.
0
Не получится. Современные компиляторы на попытку задекларировать в одном порядке а в конструкторе инитиализировать в другом скажут варнинг ^_^.
0
Так не пишет ни один вменяемый C++ программист, к чему приводить надуманные примеры?
+2
Ничего. :) Деструктор не вызовется.
0
struct Foo
{
boost::scoped_array<char> data;
Foo()
{
File file("somefile");
data.reset(new char[file.length()]);
file.read(data.get(), file.length());
}
};
Хотя бы так. И деструктор не нужен.
+1
UFO just landed and posted this here
Да, компилятор создаст тривиальный деструктор, в котором будут вызваны деструкторы всех членов класса и базового класса. Но я не понимаю, какое это имеет отношение к приведенному примеру кода — в нём, даже если File::File бросит исключение, всё останется в валидном состоянии и память нигде не утечёт.
Про вызов деструкторов несконструированных мемберов вы, мягко говоря, неправду сказали.
Про вызов деструкторов несконструированных мемберов вы, мягко говоря, неправду сказали.
0
UFO just landed and posted this here
Это было бы удобно. А костыль прикрутили бы в другом, не так часто используемом месте. В любом случае, я согласился что формулировка чуток резковата и исправил ее на более нейтральную :).
0
Теперь понятно. Я почему-то думал, что вы утверждаете, что деструкторы будут вызываться, а вы просто привели пример показывающий, почему их вызывать не надо. Или я опять вас неправильно понял?
+1
Но разве правильно запрашивать ресурсы прямо в конструкторе? Если просто логически разделить конструкцию класса и его инициализацию — проблема исчезнет.
-1
Правильно в том случае если классу эти ресурсы необходимы для работы. Например если у нас класс файл — то логично в конструкторе открыть этот самый файл, а если класс сокет — то создать объект сокет.
Так мы гарантируем — что если объект класса создан — то он рабочий. Иначе же придется каждый раз писать
что сводит С++ к Си с классами. Можно конечно писать и так — но зачем если есть более удобные и логичные способы.
Так мы гарантируем — что если объект класса создан — то он рабочий. Иначе же придется каждый раз писать
Socket s;
if(s.is_valid())...
что сводит С++ к Си с классами. Можно конечно писать и так — но зачем если есть более удобные и логичные способы.
+2
Про плюсы говорить не буду, но в Delphi работает так:
1. При создании память объекта заполняется нулями.
2. При выбросе исключения в конструкторе автоматически вызывается деструктор.
3. Деструктор умеет уничтожать недосозданный объект (благодаря пункту 1).
В результате бросать исключение в конструкторе можно без всяких ограничений.
1. При создании память объекта заполняется нулями.
2. При выбросе исключения в конструкторе автоматически вызывается деструктор.
3. Деструктор умеет уничтожать недосозданный объект (благодаря пункту 1).
В результате бросать исключение в конструкторе можно без всяких ограничений.
+1
>>деление на ноль… все это может случиться по разным причинам, начиная от вышедшего из строя железа и заканчивая вирусами в системе, но как правило от нас и нашей программы это не очень зависит.
Уж деление на ноль зависит от программиста напрямую, он просто обязан это предугадывать. По крайней мере именно этому нас учили с первого курса университета
Уж деление на ноль зависит от программиста напрямую, он просто обязан это предугадывать. По крайней мере именно этому нас учили с первого курса университета
0
Оно может произойти из-за срыва стека, битой памяти, в чужй библиотеке :). Обртите внимание, что я эти шибки рекомендую классифицировать как неожиданные и при встрече с ними в production честно падать. Потому как это будет либо ошибка в нашей логике либо что-то из вышеперичсленного, продолжать работать мы после такого не хотим.
+1
Я могу сказать почему коды выжили в PHP. Потому что нет плагина для IDE, который может статически проанализировать код и ругнуться, что ты мол не поймал такой-то Exception. А плагина в свою очередь нет, так как это очень не тривиальная задача проанализировать интерпретируемый код с нестрогой типизацией.
0
Для питона тоже такого IDE нету.
Однако исключения используются весьма широко и ловко (например, для обозначения конца списка в итераторе)
Однако исключения используются весьма широко и ловко (например, для обозначения конца списка в итераторе)
0
Если для питона разработан набор методик, то это хорошо. В принципе в PHP мы тоже используем локальные исключения, но прокидывать их далеко опасаемся.
Например вы скачали библиотеку для работы с какой-нибудь хренью. Интегрировали. А потом выясняется что на продакшене пользователи нередко видят вместо сайта сообщение об ошибке. Ибо некоторые функции из этой библиотеки также кидают свои исключения в случае оборванного соединения с треть сторонним сервисом и ещё в каком случае.
И конечно же примеры по пользованию этой библиотекой не содержат секции try — catch с описание возможных проблем, а если и содержат то не все. И это не только потому что автор библиотеки разгильдяй, но и потому, что самому автору сложно однозначно ответить какие исключения могут быть вызваны внутри кода. И это сплошь и рядом.
Например вы скачали библиотеку для работы с какой-нибудь хренью. Интегрировали. А потом выясняется что на продакшене пользователи нередко видят вместо сайта сообщение об ошибке. Ибо некоторые функции из этой библиотеки также кидают свои исключения в случае оборванного соединения с треть сторонним сервисом и ещё в каком случае.
И конечно же примеры по пользованию этой библиотекой не содержат секции try — catch с описание возможных проблем, а если и содержат то не все. И это не только потому что автор библиотеки разгильдяй, но и потому, что самому автору сложно однозначно ответить какие исключения могут быть вызваны внутри кода. И это сплошь и рядом.
+1
В веб-разработке вообще должен быть особый продход к исключеиням.
Разработчик библиотеки должен как минимум документировать те исключения которые кидает его код.
А при использовании третьих библиотек прочитать в документации какие исключения могут вывалиться оттуда (и подумать что с ними можно сделать).
Если так не сделано, то не стоит называть это библиотекой и темболее куда-то интегрировать.
А автор таки либо разгильдяй, либо не умеет читать.
Разработчик библиотеки должен как минимум документировать те исключения которые кидает его код.
А при использовании третьих библиотек прочитать в документации какие исключения могут вывалиться оттуда (и подумать что с ними можно сделать).
Если так не сделано, то не стоит называть это библиотекой и темболее куда-то интегрировать.
А автор таки либо разгильдяй, либо не умеет читать.
0
>> Если так не сделано, то не стоит называть это библиотекой и темболее куда-то интегрировать.
Очень правильные пафосные слова, но…
Мы не живём в идеальном мире. Мало что хорошо документировано. Даже nginx, например, так что не использовать его теперь что-ли.
Очень правильные пафосные слова, но…
Мы не живём в идеальном мире. Мало что хорошо документировано. Даже nginx, например, так что не использовать его теперь что-ли.
0
Неужто в мире php настолько мало хорошего кода, что приходится юзать поделки и изучать их методом тыка?
А что в nginx недокументировано?
А что в nginx недокументировано?
0
>> Неужто в мире php настолько мало хорошего кода, что приходится юзать поделки и изучать их методом тыка?
Отнюдь. Наиболее часто сюрпризы с Exception выдаёт ZendFramework, а это далеко не поделка.
Я просто пытаюсь объяснить, что отказ от эксепшенов чаще проще, чем их использование.
>> А что в nginx недокументировано?
Плохая английская документация, как минимум. Плюс навигация по документации не очень. Плохо структурировано.
Отнюдь. Наиболее часто сюрпризы с Exception выдаёт ZendFramework, а это далеко не поделка.
Я просто пытаюсь объяснить, что отказ от эксепшенов чаще проще, чем их использование.
>> А что в nginx недокументировано?
Плохая английская документация, как минимум. Плюс навигация по документации не очень. Плохо структурировано.
0
Настоящие профессионалы совмещают оба варианта.
svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java
Искать «return exception».
svn.apache.org/repos/asf/tomcat/trunk/java/org/apache/catalina/servlets/DefaultServlet.java
Искать «return exception».
+4
Не могу сказать, что это хороший пример. Вся выгода — только в том, что не нужно явно писать try/catch (один раз!), да сам принцип подходит только для случая, когда метод ничего возвращает. Более того, в данном коде при вызове copyRange(...) внутри getReadme(...) произошло неявное глушение исключения, что трудно назвать здравой идеей. В общем, не убедили.
0
Всегда пологал, что пользоваться кодами возврата в высокоуровневых языках для обработки ошибок — больше похоже на «концепцию magic numbers». Сам использую исключения (в общих чертах) примерно так:
# все ожидаемые ошибки
class AppError < StdError
end
# более точное описание
class IntegrationError < AppError
end
Возможно, автор может написать небольшой обзор конвенции, к которой он пришел и пользуется. Интересно услышать мнение. Спасибо.
# все ожидаемые ошибки
class AppError < StdError
end
# более точное описание
class IntegrationError < AppError
end
Возможно, автор может написать небольшой обзор конвенции, к которой он пришел и пользуется. Интересно услышать мнение. Спасибо.
0
Автор старается использовать что лучше подходит для решаемой задачи :). Автору приходится решать много разнообразных задач, начиная от драйверов на чистом C и заканчивая DSL на ruby :(
+1
Тем более есть о чем написать. На примерах.
0
Кстати говоря, например, на рельсах при сохранении в AR есть такая отличная возможность выбирать метод save или save!, что дает возможность выбрать вариант возвращения ошибки. Если нужно обрабатывать возможно неудачный результат работы, то предпочитаю save, а если результат сохранения будет ошибочным только в экстренном случае, то использую save! и в таком случае уже обрабатываю как исключение и заношу в таблицу исключений в БД.
Это еще один хороший пример того как можно комбинировать и то и другое.
Это еще один хороший пример того как можно комбинировать и то и другое.
+1
В реализации исключений на С++ есть другой фатальный недостаток — это нечёткая спецификация ислучений в функциях, вот корень всех зол. Если у функции специфицированы исключения, то это не даёт никаких гарантий что она бросает только их.
0
На мой взгляд с сиключениями две проблемы, из за которых их осознано или не очень избегают:
1. Они дают рваный поток выполнения. То есть по коду (даже в статически типизированных языках) нельзя сказать куда улетит брошенное нами исключение и что может вывалится из того, что мы вызвали. Код оказывается между молотом из исключений которые бросили в него и наковальней из интерфейса который он должен поддержать.
2. Даже в статически типизированных языках с поддержкой checked exceptions язык практически не помогает в какой-то спецификации обработки ошибок. Дело в том что реальный путь исключения по стэку (с.м. п. 1) определяется не статическим описанием программы (сигнатуры методов с исключениями) а её динамической структурой (кто какие реализации вызывает).
3. Предыдущие два пункта дают совсем прохой синергетический эффект — по коду трудно понять начальный замысел автора по обработке ошибок. А если чего-то не понимаешь, то это и легко разрушить.
В общем по-моему история распространения исключений по разным языкам — это ещё один пример того, как добро выдернутое из контекста обращается во зло.
1. Они дают рваный поток выполнения. То есть по коду (даже в статически типизированных языках) нельзя сказать куда улетит брошенное нами исключение и что может вывалится из того, что мы вызвали. Код оказывается между молотом из исключений которые бросили в него и наковальней из интерфейса который он должен поддержать.
2. Даже в статически типизированных языках с поддержкой checked exceptions язык практически не помогает в какой-то спецификации обработки ошибок. Дело в том что реальный путь исключения по стэку (с.м. п. 1) определяется не статическим описанием программы (сигнатуры методов с исключениями) а её динамической структурой (кто какие реализации вызывает).
3. Предыдущие два пункта дают совсем прохой синергетический эффект — по коду трудно понять начальный замысел автора по обработке ошибок. А если чего-то не понимаешь, то это и легко разрушить.
В общем по-моему история распространения исключений по разным языкам — это ещё один пример того, как добро выдернутое из контекста обращается во зло.
+5
Блин увлёкся. Это я к чему начинал… В сообществе функциональщиков есть интересные наработки ([1], [2]) по обращению с ошибками, счетающие в себе явность потока управления и возможность не думать об ошибке пока не захочется. И кажется эти наработки потенциально портабельны в промышленные языки и применимы.
P.S. Я знаю ссылки ужасны. Поверьте, это всё можно изложить ещё в 10 раз менее понятно.
P.S. Я знаю ссылки ужасны. Поверьте, это всё можно изложить ещё в 10 раз менее понятно.
0
Когда начинал изучать Яву, показалось, что там слишком много исключений — там, где можно было бы обойтись кодами возврата (открытие несуществующего файла — к примеру). По прошествии… пяти? лет — все еще считаю, что исключений слишком много ;) Хотя сам активно их использую…
0
В Java своя особая проблема — checked exceptions — когда компилятор заставляет программиста вставлять блоки обработки исключений в принудительном порядке. Сама по себе идея не такая уж и страшная, но очень уж много методов в стандартной библиотеке языка злоупотребляют ими. В результате такая простая задача, как скопировать содержимое файла в другой, превращается в огромную простыню catch-блоков, в порядке которых и уровнях их вложенности очень легко запутаться неподготовленному человеку.
+1
Да, согласен. Было бы хорошо, если бы можно было как-то коротко и явно «глушить» (превращать в unchecked) некоторые исключения при вызове конкретных методов. Аннотациями, например.
В 7-й Java добавили наконец-то возможность в одном catch ловить сразу несколько исключений, так что теперь простыни должны стать чуть меньше.
В 7-й Java добавили наконец-то возможность в одном catch ловить сразу несколько исключений, так что теперь простыни должны стать чуть меньше.
0
ну, тут есть одна хитрость — которая иногда помогает ;) можно написать отдельный catch на те эксепшены, которые надо перехватывать — а на остальные повесить просто catch(Exception) — или вообще, Throwable. к примеру, FileNotFoundException extends IOException — так что достаточно одного catch(IOException) в блоке обработки… но в целом, соглашусь с Вами — многие checked можно было бы пускать через RuntimeException.
0
За перехват ВСЕХ исключений обычно больно бьют, в лучшем случае на ревью.
0
если это «тупой перехват» — с целью не возиться, тогда я бы сам вдарил ;) а если известно, что логика для всех возможных исключений — одна и та же, и они все наследуются от общего предка — тогда зачем на каждый класс писать одинаковый обработчик? в Яве, к примеру (кто про что, а шелудивый — про баню ;) от Exception наследуются ожидаемые исключения (которые требуют объявления в интерфейсе метода), а неожиданные — это RuntimeException, которым объявление не требуются. так вот, если я знаю, что у меня в коде могут возникнуть три вида ожидаемых исключений — и на все их я буду одинаково реагировать, я просто перехватываю Exception — а переполнение стека или что-нибудь еще такое вылетит у меня по RuntimeException ;) всегда мы упираемся в вопрос проектирования — в значительной степени, это касается базовых классов и библиотек. которые, к сожалению, слишком часто пишутся такими же людьми, как мы сами ;)
+1
Верно написано, но при использовании friend-функций имхо все таки удобнее пользоваться кодами.
0
Коды возврата выжили совсем по другим причинам.
1. Исключения просто менее эффективны: либо требуют много дополнительной памяти на таблицы с identity, либо дополнительных записей в память, при чём при каждом вызове функции, что создаёт существенные накладные расходы.
2. Исключениями большинство нормально не умеет пользоваться. Попробуйте объяснить человеку, что это не механизм возврата кодов ошибок, а именно механизм исключительных ситуаций, которые разработчик алгоритма просто не может предусмотреть (например, он пишет обобщённый список). И исключения не должны быть частью API библиотеки необобщённых алгоритмов (шаблоны там всякие) вообще. И вследствии этого, всё выглядит красиво на маленьких примерах, а в большой программе исключения — это полный вынос мозга.
3. Ну. И вообще, исключения — это вообще-то очень древний и корявый способ. Человечество как бы уже додумалось до сопроцедур (coroutines), до продолжений (continuations) и прочих механизмов, которые дают понятную логику с возвратом значений, на далёкий уровень стека вызовов: основное важное качество исключений.
1. Исключения просто менее эффективны: либо требуют много дополнительной памяти на таблицы с identity, либо дополнительных записей в память, при чём при каждом вызове функции, что создаёт существенные накладные расходы.
2. Исключениями большинство нормально не умеет пользоваться. Попробуйте объяснить человеку, что это не механизм возврата кодов ошибок, а именно механизм исключительных ситуаций, которые разработчик алгоритма просто не может предусмотреть (например, он пишет обобщённый список). И исключения не должны быть частью API библиотеки необобщённых алгоритмов (шаблоны там всякие) вообще. И вследствии этого, всё выглядит красиво на маленьких примерах, а в большой программе исключения — это полный вынос мозга.
3. Ну. И вообще, исключения — это вообще-то очень древний и корявый способ. Человечество как бы уже додумалось до сопроцедур (coroutines), до продолжений (continuations) и прочих механизмов, которые дают понятную логику с возвратом значений, на далёкий уровень стека вызовов: основное важное качество исключений.
+2
В целом с этим тоже соглашусь. В частности добавлю:
На мой взгляд, большая часть кода, который мы пишем, не требует какой-то особой эффективности :). Задачи, требующие эффективности в целом — например, ядро linux, как правило пишется на специально предназначенных для этого языках — например, C :).
Оно сейчас, увы, не в мейнстриме :(.
Исключения просто менее эффективны: либо требуют много дополнительной памяти на таблицы с identity, либо дополнительных записей в память, при чём при каждом вызове функции, что создаёт существенные накладные расходы.
На мой взгляд, большая часть кода, который мы пишем, не требует какой-то особой эффективности :). Задачи, требующие эффективности в целом — например, ядро linux, как правило пишется на специально предназначенных для этого языках — например, C :).
И вообще, исключения — это вообще-то очень древний и корявый способ. Человечество как бы уже додумалось до сопроцедур (coroutines), до продолжений (continuations) и прочих механизмов, которые дают понятную логику с возвратом значений, на далёкий уровень стека вызовов: основное важное качество исключений
Оно сейчас, увы, не в мейнстриме :(.
+1
1) а Си++ менее эффективен чем просто Си: требует дополнительной памяти для RTTI, не говоря уже о дополнительных расходах при вызове функций — членов класса.
2) классами большинство нормально не умеет пользоваться. период.
))
2) классами большинство нормально не умеет пользоваться. период.
))
0
1. Ну… Си++ — монструазная штука и хорошо оптимизируемая (куча народа над этим работает). Поэтому, например, при помощи шаблонов Си обогнать вполне реально. Другое дело, что Си++ — это такой Си++, там всё себе вывихнешь, пока нормальный по скорости код получишь.
2. Поддерживаю.
2. Поддерживаю.
0
не говоря уже о дополнительных расходах при вызове функций — членов класса.виртуальных функций.
0
Как раз в больших программах исключения много лучше кодов ошибок — так как выдают место ошибки вкупе со стеком. Коды ошибок что-то говорят только маааааленькому ксуочку вызывающего кода, в котором тоже могут быть ошибки. В результате большая программа падает в месте, от которого до места ошибки как до Китая пешком.
Меньшая эффективность имеет смысл, только если исключительных ситуаций у вас много, то тогда проблема явно не в исключениях, а в логике на них.
Так что единственный недостаток исключений в прикладном программировании — это неумение ими пользоваться.
Только соппроцедурами, продолжениями и т.п. умеют пользоваться еще меньше народу.
Меньшая эффективность имеет смысл, только если исключительных ситуаций у вас много, то тогда проблема явно не в исключениях, а в логике на них.
Так что единственный недостаток исключений в прикладном программировании — это неумение ими пользоваться.
Только соппроцедурами, продолжениями и т.п. умеют пользоваться еще меньше народу.
+1
Всё, конечно, зависит от контекста задачи. Для большой и сложной системы более применимы исключения. Для лёгкой и требовательной к производительности задаче коды ошибок скорее всего будут наилучшим выбором. Поэтому для меня будет странным увидеть документ «описания кодов ошибок» в проекте на C# с кучей классов, но в программе управления ДВС ракеты-носителя я бы скорее удивился обратному.
Естественно, необходимо учитывать, что программы пишут люди разного уровня, привычек и склада ума. Конечно, обычно программисты имеют схожий склад ума, но всё же. Исключения, как правило, проще поддерживать, расширять, менять поведение. С кодами исключений всё сложнее, и чем больше проект, тем сложность возрастает быстрее.
Вообще, по-моему, тут не стоит вопрос что использовать, тут стоит ответ на вопрос «какая основная цель у Вашей системы — расширяемость и поддерживаемость или риалтамовость?»
Естественно, необходимо учитывать, что программы пишут люди разного уровня, привычек и склада ума. Конечно, обычно программисты имеют схожий склад ума, но всё же. Исключения, как правило, проще поддерживать, расширять, менять поведение. С кодами исключений всё сложнее, и чем больше проект, тем сложность возрастает быстрее.
Вообще, по-моему, тут не стоит вопрос что использовать, тут стоит ответ на вопрос «какая основная цель у Вашей системы — расширяемость и поддерживаемость или риалтамовость?»
0
Да ну. В большой и сложной системе исключения, повторюсь, это — вынос мозга. С ними невозможно будет программировать. А вот в realtime задаче нечто похожее на исключения может быть вполне выгодным, потому что в случае ошибки, управление будет передано на обработчик, возможно, быстрее (зависит от реализации).
0
А у меня противоположное мнение: в большой системе только с исключениями и можно прожить. По опыту полумиллионострокового проекта на Delphi.
+3
Да ну. В большой и сложной системе исключения, повторюсь, это — вынос мозга.Мягко говоря, спорное утверждение. Уж не знаю про какой язык речь, но в большинстве известных мне всё как раз наоборот, имхо. Если про ООП говорить, то тем более. Исключения как раз гораздо более подходят для полноценной абстракции, чем коды возврата. Например, в java это и просто не принято и неудобно совершенно. Возврат null в некоторых случаях допустим, например, когда отсутствие объекта само по себе не ошибка ну и т. д., от реализации зависит.
+2
Надо их уметь готовить. Я, собственно, и статью написал дабы обсудить с сообществом способы приготовления. А то вот недавно смотрел код серьезного open source проекта, так там данные по сети отправлялись в деструкторе по срабатыванию исключения (это у них был штатный способ отправлять данные). Смотришь на такое и понимаешь что зря, зря они селедку в шоколаде запекали :).
+3
Ну… Не знаю. Согласен, что утверждение спорное. Просто самый замечательный экспириенс у меня был с исключениями, когда руководитель проекта сказал: никаких исключений в интерфейсах, за оные буду лишать части зарплаты. И всё так сразу стало красиво и понятно. Даже внутренняя логика модулей от этого сильно выиграла, там было, в основном, try-catch в теле реализации метода интерфейсного класса, и все знали, что дальше оно не вылетит, и не лепили поэтому try-catch где-то внутри.
Всё было легко и просто. Кажется, кстати, в Go принята такая же политика обработки исключений.
Так что, по-моему, коды возврата — это благо для больших систем, когда модули явно друг другу говорят, что с ними не так происходит. API операционок и, скажем, POSIX-овская модель работы с процессами, которая весьма удобна, тому подтверждение: внутри процесс может по своему стеку хоть на ассемблере гонять, но наружу он выдаёт код возврата, и это удобно для программирования.
Всё было легко и просто. Кажется, кстати, в Go принята такая же политика обработки исключений.
Так что, по-моему, коды возврата — это благо для больших систем, когда модули явно друг другу говорят, что с ними не так происходит. API операционок и, скажем, POSIX-овская модель работы с процессами, которая весьма удобна, тому подтверждение: внутри процесс может по своему стеку хоть на ассемблере гонять, но наружу он выдаёт код возврата, и это удобно для программирования.
+1
С ними невозможно программировать, простите, только тем, кто исключениями пользоваться не умеет.
0
Основная беда С++ в отсутствии finally, я считаю. Да, можно написать костыль, но это будет уже не то. Поэтому в плюсах активно юзать исключения — нецелесообразно.
+1
Зачем в С++ finally, если в C++ есть RAII?
0
Не всегда удобно засовывать освобождение ресурсов в деструкторы. Пример — подтверждение или откат транзакции БД.
0
Собссно я к тому и клоню, что в С++ без исключений можно вполне прожить. Только надо принять решение — используем или не используем вообще.
Что доставляет неудобства в отсутствие исключений — так это невозможность централизованной обработки исключительных ситуаций и сложность с передачей сложного описания исключения.
Что доставляет неудобства в отсутствие исключений — так это невозможность централизованной обработки исключительных ситуаций и сложность с передачей сложного описания исключения.
0
Пример, когда нужен и код ошибки и исключение:
Ситуация — звоним на телефон и проигрываем файл
public PlayFileResult PlayFile(string file) {… }
Пусть будет три состояния результата операции:
1) Оборудование сдохло
2) На другом конце провода факс
3) Ответил человек
Очевидно, что в первом случае надо бросить ошибку (вызвать исключение), во вторых двух надо вернуть какой-то код.
Ситуация — звоним на телефон и проигрываем файл
public PlayFileResult PlayFile(string file) {… }
Пусть будет три состояния результата операции:
1) Оборудование сдохло
2) На другом конце провода факс
3) Ответил человек
Очевидно, что в первом случае надо бросить ошибку (вызвать исключение), во вторых двух надо вернуть какой-то код.
+1
Вопрос сложный, но интересный. Я бы сказал так:
1. В зависимости от приложение сдохшее оборудование может быть как неожиданной ошибкой (у нас пользовательское приложение которое работает со штатной звонилкой системы) так и ожидаемой (у нас сервис на АТС, от нее постоянно что-то отваливается). Соответственно, кидаем разные исключения.
2. Факс / человек на другом конце провода — это вообще не ошибка, это результат работы функции :). И мы его естественно возвращаем. Использовать исключение для результата было бы… хмм… не по феншую.
1. В зависимости от приложение сдохшее оборудование может быть как неожиданной ошибкой (у нас пользовательское приложение которое работает со штатной звонилкой системы) так и ожидаемой (у нас сервис на АТС, от нее постоянно что-то отваливается). Соответственно, кидаем разные исключения.
2. Факс / человек на другом конце провода — это вообще не ошибка, это результат работы функции :). И мы его естественно возвращаем. Использовать исключение для результата было бы… хмм… не по феншую.
+1
знаешь, а меня вот как пользователя жутко бесит когда один отвалившийся модуль программы рушит всю программу. а чтобы этого не произошло — приходится каждое обращение к модулю заворачивать в try-catch-и. и чем более модульная система, тем страшнее получается код.
0
Зависит от программы. Если у нас Photoshop и в модуле не сработала API функция CreateEvent() — мы, конечно, можем это завернуть и попытаться продолжить работу. Но тут есть тонкий момент — неожиданные ошибки они на то и неожиданные, что при нормальной работе программы они возникать не должны. А если возникли — значит что-то пошло не так. И продолжить работу может быть не лучшим вариантом — если не сработал CreateEvent() то скорее всего у системы пушной зверь с ресурсами и она скоро рухнет. Дадим пользователю продолжить работу — он захочет сохранить файл — и следующая ошибка такого рода может привести к его повреждению. А мы совсем не хотим чтобы у пользователя повредились данные :). Вот по этому и приходится падать с громким стуком O_O.
+2
и терять несохранённые данные? классное решение. а чтобы данные не повреждались нужно использовать транзакции. записали, проверили, стёрли предыдущую версию.
я не знаю что-то кое CreateEvent, но если он падает только в конкретном модуле, потому что он как-то не так его вызывает, то пусть и дохнет этот самый модуль, а не всё приложение целиком.
я не знаю что-то кое CreateEvent, но если он падает только в конкретном модуле, потому что он как-то не так его вызывает, то пусть и дохнет этот самый модуль, а не всё приложение целиком.
0
Чтобы не терять несохраненные данные придумали автосейв :).
Функция Windows API которая аллоцирует объект межпоточной синхронизации. Обламывается, как правило, когда к системе приходит пушной зверь и заканчиваются ресурсы.
я не знаю что-то кое CreateEvent, но если он падает только в конкретном модуле, потому что он как-то не так его вызывает, то пусть и дохнет этот самый модуль, а не всё приложение целиком.
Функция Windows API которая аллоцирует объект межпоточной синхронизации. Обламывается, как правило, когда к системе приходит пушной зверь и заканчиваются ресурсы.
+2
Приложение, которому настолько критично сохранение данных — вообще не должно иметь несохраненных. Классический пример — СУБД Oracle. Ее лог транзакций пишется синхронно с операцией и позволяет восстановить состояние базы даже после полного песца в рамках системы. Коды ошибок тут скорее вредны чем бесполезны.
0
ну, всегда надо искать балланс между сохранностью данных и скоростью работы. только вот отказываться от сохранения не потому что это невозможно, а потому что это _может_ не получиться — глупо. надо хотябы попытаться. а сбой сохранения всегда может произойти и надо быть к этому готовым и позаботиться о бэкапах/журналах заранее.
0
Почему это всё очевидно? Если вы предусматриваете все эти состояния, ну сделайте код возврата, да и всё. Не нужно тут никакого исключения. Исключение нужно тогда, когда вы никакого иного интерфейса не можете предложить. Вот если бы было нечто такое:
public playfileresult playfile(musicfeed feed) {… }
И проигрывался бы некий musicfeed, в интерфейсе которого не предусмотренно, что всё может отвалиться, потому что магнитофон зажевал плёнку, то вы пишете свой feed с выкидыванием исключений, и анализируете их тут же, вместе с кодами возврата специфичными для playfile.
В этом основная идея исключений, а не в том, чтобы лепить на них вообще всю обработку ошибок, как в Java сделано.
Исключения они для того и нужны чтобы организовать непредусмотренный (по разным причинам, иногда невозможно предусмотреть) авторами кода возврат из него. То есть, это вообще механизм для обобщённого программирования, а не для обработки ошибок. Страуструп вообще должен жевать свои тапочки за то, что настаивает на использовании исключений всегда и везде.
public playfileresult playfile(musicfeed feed) {… }
И проигрывался бы некий musicfeed, в интерфейсе которого не предусмотренно, что всё может отвалиться, потому что магнитофон зажевал плёнку, то вы пишете свой feed с выкидыванием исключений, и анализируете их тут же, вместе с кодами возврата специфичными для playfile.
В этом основная идея исключений, а не в том, чтобы лепить на них вообще всю обработку ошибок, как в Java сделано.
Исключения они для того и нужны чтобы организовать непредусмотренный (по разным причинам, иногда невозможно предусмотреть) авторами кода возврат из него. То есть, это вообще механизм для обобщённого программирования, а не для обработки ошибок. Страуструп вообще должен жевать свои тапочки за то, что настаивает на использовании исключений всегда и везде.
+1
Механизм исключений как раз очень удобен, чтобы не городить всевозможных Result'ов. При нормальном функционировании метод возвращает свое успешное значение, а если что-то пошло не так — конкретное исключение, прописанное в заголовке метода (это я про java говорю). Таким образом логика нормального фукнционирования не усложняется лишними проверками if (result!=ERROR), а обработка ошибок вынесена в отдельный блок.
0
Это когда пишется необольшое приложение для себя — это да, логика не усложняется. Но если это сложное приложение с кучей библиотек — не будет в нём всё так красиво. Не будет никаких общих блоков обработки ошибок, потому что это не возможно, потому что в большинстве случаев отказ какого-нибудь компонента не должен приводеть к прерыванию всей цепочки работы (чего-нибудь клиент-серверное к примеру).
Кроме того, а почему Вы считаете, что 100500 кодов возвратов будут хуже, чем 100500 значений для исключения?
Кроме того, а почему Вы считаете, что 100500 кодов возвратов будут хуже, чем 100500 значений для исключения?
0
Считаю, что исключения стоит использовать действительно в исключительных ситуациях. Более того, разумно использовать исключение только в том случае — если знаешь как его обработать. Сам по себе блок catch (Exception ex) не имеет смысла, мы всего лишь знаем, что возникло исключение, а чем оно вызвано и что делать дальше… Другое дело, например, catch (FileNotFoundException ex) — сразу ясно что за ошибка и чем она вызвана. Считаю, что прежде чем прибегать к использованию исключений — можно выполнить те или иные проверки, которые позволят избежать возникновения исключительных ситуаций (например, проверить наличие файла, прежде чем пытаться прочесть его). Лучший вариант, на мой взгляд, использовать коды ошибок+исключения. Естественно, если для проверки валидности той или иной операции потребуются 15 вложенных if-оф, логичнее обернуть блок кода в try-catch, но лепить их везде, уповая на высокую производительность машин — неправильно, имхо.
+2
Где найти пример правильного использования исключений в C++?
Я искренне не понимаю следующие моменты:
Если я вызываю функцию, то неужели я должен знать все возможные исключения, которые эта функция может выбросить, включая все те исключения, которые бросают вложенные функции и библиотеки, которыми пользуется данная реализация функции, а как насчет того, что реализация может изменится?
То есть исключения не могут входить в интерфейс функции?
Если не заботиться о обработке исключений при непосредственном вызове функций, то вероятно нужно обрабатывать их на каких-то определенных слоях системы, но тут возникает тот-же вопрос: «Как гарантировать то, что все исключения были обработаны»? Как насчет новых типов исключений?
Если кто-то добавляет новое исключение в низкоуровневую функцию, то как это исключение нужно обработать? А что если эта функция используется в нескольких проектах?
Как программировать и сопровождать код с исключениями?
А еще как убедить программистов не прятать свои же ошибки с использованием конструкции catch(...)?
Я искренне не понимаю следующие моменты:
Если я вызываю функцию, то неужели я должен знать все возможные исключения, которые эта функция может выбросить, включая все те исключения, которые бросают вложенные функции и библиотеки, которыми пользуется данная реализация функции, а как насчет того, что реализация может изменится?
То есть исключения не могут входить в интерфейс функции?
Если не заботиться о обработке исключений при непосредственном вызове функций, то вероятно нужно обрабатывать их на каких-то определенных слоях системы, но тут возникает тот-же вопрос: «Как гарантировать то, что все исключения были обработаны»? Как насчет новых типов исключений?
Если кто-то добавляет новое исключение в низкоуровневую функцию, то как это исключение нужно обработать? А что если эта функция используется в нескольких проектах?
Как программировать и сопровождать код с исключениями?
А еще как убедить программистов не прятать свои же ошибки с использованием конструкции catch(...)?
+2
Если я вызываю функцию, то неужели я должен знать все возможные исключения, которые эта функция может выбросить, включая все те исключения, которые бросают вложенные функции и библиотеки, которыми пользуется данная реализация функции, а как насчет того, что реализация может изменится?
То есть исключения не могут входить в интерфейс функции?
По феншую, мы ловим один тип исключения — «ожидаемая ошибка». Соответственно, если нам не нужна детализация (а это 99% случаев), то исключение ожидаемой ошибки будет корнем иерархии исключений ожидаемых ошибок. Ловим мы его как правило выше вызовов функций, в достаточно большом логическом блоке на который мы хотим среагировать — наприимер, показать пользователю сообщение.
Второй тип исключений, неожиданная ошибка, мы либо не ловим (честно падаем) либо ловим очень высоко и перезапускаемся / предлагаем отправить багрепорт.
Если не заботиться о обработке исключений при непосредственном вызове функций, то вероятно нужно обрабатывать их на каких-то определенных слоях системы, но тут возникает тот-же вопрос: «Как гарантировать то, что все исключения были обработаны»? Как насчет новых типов исключений?
Иерархия исключений должна быть спроектирована таким образом, что ожидаемые и неожиданные ошибки находятся в коре иерархии — их и ловим, если не нужна детализация. Увы, не все проектировщики языков и API об этом знают :(.
Если кто-то добавляет новое исключение в низкоуровневую функцию, то как это исключение нужно обработать? А что если эта функция используется в нескольких проектах?
Как я уже писал, ключ к победе — это понимание, что исключения глобально двух типов — «ожидаемые» и «неожиданные». Если эти типы — классы исключений, лежащие в корне иерархии, то все что нам нужно — это отнаследовать добавляемое исключение от нужного.
Как программировать и сопровождать код с исключениями?
В большинстве языков, даже в C++, для этого есть возможность в сигнатуре методов прописовать что они кидают. Ну а иерархия исключений показывает что из кидаемого ожидаемое а что — нет.
А еще как убедить программистов не прятать свои же ошибки с использованием конструкции catch(...)?
Поставить какую-нибудь утилиту на сервер чтобы она не допускала коммиты с таким кодом :). Дешего и сердито.
+1
то есть степень ожидаемости исключений целиком и полностью определяется не программистом, а автором языка?
0
Спасибо за ответ, многое прояснилось и с виду вроде логично (не хватает кармы чтобы поставить плюс).
Еще хотелось бы уточнить:
Существуют ли стандартные иерархии исключений в C++, или в каждом проекте нужно придумывать свою иерархию классов, стоит ли наследовать ее от чего-нибудь вроде std::exception?
Еще хотелось бы уточнить:
Существуют ли стандартные иерархии исключений в C++, или в каждом проекте нужно придумывать свою иерархию классов, стоит ли наследовать ее от чего-нибудь вроде std::exception?
0
Попробую потроллить :-)
Топик на засыпку: «Реализация механизма исключений средствами аспектно-ориентированного программирования».
Топик на засыпку: «Реализация механизма исключений средствами аспектно-ориентированного программирования».
+1
на самом деле, мне кажется, выбор между исключениями и кодами возврата заключается в ответе на вопрос: где будет обрабатываться ошибка(нестандартная ситуация)? если в той же части кода, что и вызывает код, генерирующий нестандартную ситуацию — тогда код возврата может быть проще. если же перехватчик может лежать гораздо выше в стеке вызова — тогда передача кодов возврата наверх может быть слишком громоздкой, поскольку потребуется проверка на каждом уровне. в моем представлении, «file not found» — это для кода возврата, а read error — это уже исключение (хотя зависит от контекста)
0
Тогда император, отец ребенка, обнародовал указ, предписывающий всем его подданным под страхом строгого наказания разбивать яйца с острого конца. Этот закон до такой степени озлобил население, что, по словам наших летописей, был причиной шести восстаний, во время которых один император потерял жизнь, а другой — корону.
…Насчитывают до одиннадцати тысяч фанатиков, которые в течение этого времени пошли на казнь, лишь бы не разбивать яйца с острого конца. Были напечатаны сотни огромных томов, посвящённых этой полемике, но книги Тупоконечников давно запрещены, и вся партия лишена законом права занимать государственные должности. В течение этих смут императоры Блефуску часто через своих посланников делали нам предостережения, обвиняя нас в церковном расколе путём нарушения основного догмата великого нашего пророка Люстрога, изложенного в пятьдесят четвёртой главе Блундекраля (являющегося их Алькораном). Между тем это просто насильственное толкование текста, подлинные слова которого гласят: Все истинно верующие да разбивают яйца с того конца, с какого удобнее.
(с) Свифт.
…Насчитывают до одиннадцати тысяч фанатиков, которые в течение этого времени пошли на казнь, лишь бы не разбивать яйца с острого конца. Были напечатаны сотни огромных томов, посвящённых этой полемике, но книги Тупоконечников давно запрещены, и вся партия лишена законом права занимать государственные должности. В течение этих смут императоры Блефуску часто через своих посланников делали нам предостережения, обвиняя нас в церковном расколе путём нарушения основного догмата великого нашего пророка Люстрога, изложенного в пятьдесят четвёртой главе Блундекраля (являющегося их Алькораном). Между тем это просто насильственное толкование текста, подлинные слова которого гласят: Все истинно верующие да разбивают яйца с того конца, с какого удобнее.
(с) Свифт.
+2
На C# исключения необходимо использовать только при возникновении ошибок («исключительных ситуаций»), которые невозможно предугадать на этапе написания кода. Все остальные пути поведения программы необходимо предусмотреть на этапе проектирования.
+1
Sign up to leave a comment.
Коды возврата vs исключения — битва за контроль ошибок