Здравствуйте, Аноним, Вы писали:
А>... по ссылке и по указателю. В чем разница? Как надо делать?
Г. Саттер, А. Александреску «Стандарты программирования на C++», стр. 158:
«73. Генерируйте исключения по значению, перехватывайте — по ссылке.»
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Перехват исключения...
От:
Аноним
Дата:
01.02.08 23:03
Оценка:
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, Аноним, Вы писали:
А>>... по ссылке и по указателю. В чем разница? Как надо делать?
Q>Г. Саттер, А. Александреску «Стандарты программирования на C++», стр. 158: Q>«73. Генерируйте исключения по значению, перехватывайте — по ссылке.»
Здравствуйте, Аноним, Вы писали:
А>И? Почему по ссылке а не по указателю?
0_о? Возможно я не понял вопроса...
Чтобы перехватить исключение по указателю, то надо сначала сгенерировать исключение-указатель. Собственно, во время ловли у вас нет выбора, перехватывать исключение по указателю или по ссылке. У вас есть альтернатива по значению либо по ссылке.
А>И? Почему по ссылке а не по указателю?
Пусть у вас функция бросает std::runtime_error. Вы можете в скобках catch написать «std::runtime_error e» (что Саттер не советует делать, действительно, зачем вам лишний раз копиктор вызывать) или «std::runtime_error const& e» (что и является общепринятой практикой). «Перехват по указателю» — это что значит? Попытка написать «catch (std::runtime_error const* e)»? Тогда у вас исключение не перехватится, ведь типы «T» и «T const*» — разные.
«const» можно не писать, если собираетесь изменить объект исключения и бросить («throw;» без ничего) его дальше.
Таки советую прочитать эту книгу (всего 200 страниц), или по крайней мере статью (всего 1 страница).
Здравствуйте, Аноним, Вы писали:
А>... по ссылке и по указателю. В чем разница? Как надо делать?
Перехватывать надо тип исключения, который был сгенерирован. Если объект-исключение был создан через new ("throw new X;"), то ловить его нужно по указателю на этот объект ("catch(X*){ ... }"). Если исключение было создано как автоматический объект ("throw X();"), то перехватывать его можно как по значению, так и по ссылке. Разница между этими подходами следующая. Если был выброшен динамически созданный объект, его нужно удалить через delete. При перехвате по значению возможен эффект срезки (когда в последовательности catch-обработчиков сначала перехватывается тип базового класса), и это иногда может приводить к нежелательным результатам. При перехвате по ссылке срезки не происходит, и через delete объект уничтожать тоже не надо. Поэтому перехват по ссылке считается более удобным и безопасным.
Михаил Белоносов пишет:
При перехвате по значению возможен эффект > срезки (когда в последовательности catch-обработчиков сначала > перехватывается тип базового класса), и это иногда может приводить к > нежелательным результатам.
Поправка: это ВСЕГДА приводит к нежелательным результатам.
Вы 0) теряете тип исключения 1) теряете данные, передаваемые в исключении
(кроме данных базового типа).
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Перехват исключения...
От:
Аноним
Дата:
02.02.08 11:29
Оценка:
Здравствуйте, Михаил Белоносов, Вы писали:
МБ>Здравствуйте, Аноним, Вы писали:
А>>... по ссылке и по указателю. В чем разница? Как надо делать?
МБ>Перехватывать надо тип исключения, который был сгенерирован. Если объект-исключение был создан через new ("throw new X;"), то ловить его нужно по указателю на этот объект ("catch(X*){ ... }"). Если исключение было создано как автоматический объект ("throw X();"), то перехватывать его можно как по значению, так и по ссылке. Разница между этими подходами следующая. Если был выброшен динамически созданный объект, его нужно удалить через delete. При перехвате по значению возможен эффект срезки (когда в последовательности catch-обработчиков сначала перехватывается тип базового класса), и это иногда может приводить к нежелательным результатам. При перехвате по ссылке срезки не происходит, и через delete объект уничтожать тоже не надо. Поэтому перехват по ссылке считается более удобным и безопасным.
Здравствуйте, <Аноним>, Вы писали:
А>... по ссылке и по указателю. В чем разница? Как надо делать?
Ну, про то, что это разные типы — уже сказали.
А по сути:
Когда кидаешь значение — объект исключения размещается где-то в статическом хранилище; этим ведает рантайм.
Интересный вопрос — сколько места отводится под бросаемый объект. Можно попробовать найти предел, кидая boost::array<char,N> варьируя N до гига
У старых компиляторов VC, где структурные исключения были поддержаны, а С++ные нет, — в MFC существовала эмуляция C++ных исключений (THROW-TRY-CATCH-CATCH_ALL). Естественно, хранить объект произвольного типа по-человечески можно только в динамическом хранилище, а в статике держать только указатель на него.
Если ты кидаешь указатель на объект, то кто-то (например, ты) должен вручную управлять владением этого объекта. catch же не знает, что с ним делать — удалять, дерегистрировать, не трогать...
Тут выбор: или контравариантность типов средствами компилятора (т.е. приведение к базе и наложение константности), или умные указатели фиксированного типа. Потому что про фактическую контравариантность shared_ptr<Base> <-- shared_ptr<Derived> или <-- shared_ptr<const Base> компилятор не знает.
Кроме того, динамическая память имеет свойство исчерпываться. Поэтому всякие критические ошибки (особенно std::bad_alloc и MFC/ATL CMemoryException) не используют динамическую память вообще. Т.е. запрещено throw new CMemoryException(), а только AfxThrowMemoryException(); — то есть, конечно, можно, но сборщик мусора в END_CATCH — как минимум, оставит утечку, а как максимум — удивится, почему указатель не равен адресу преждерождённого статического объекта.
Кстати говоря, улавливаемый тип (в catch) соотносится с формальным типом в выражении throw, а не с фактическим!
Маленький натурный эксперимент:
b @00427928 | f @0042792C
struct bar& @0012FF24 is struct bar throw b - брошена копия
struct bar* =00427928 is struct bar throw &b - брошен указатель типа bar (указывает на b)
struct foo& @0012FF24 is struct foo throw emit_ref() - брошена копия, при броске произошла срезка
struct foo* =00427928 is struct bar throw emit_ptr() - брошен указатель типа foo (указывает на b)
Отсюда видно: если мы хотим устроить полноценный полиморфизм времени исполнения — то должны бросать указатели (желательно, умные — чтобы самим не возиться с памятью) и затем вручную диспетчеризовать, как душе угодно.