Перехват исключения...
От: Аноним  
Дата: 01.02.08 22:09
Оценка:
... по ссылке и по указателю. В чем разница? Как надо делать?
Re: Перехват исключения...
От: Qbit86 Кипр
Дата: 01.02.08 22:56
Оценка:
Здравствуйте, Аноним, Вы писали:

А>... по ссылке и по указателю. В чем разница? Как надо делать?


Г. Саттер, А. Александреску «Стандарты программирования на C++», стр. 158:
«73. Генерируйте исключения по значению, перехватывайте — по ссылке.»
Глаза у меня добрые, но рубашка — смирительная!
Re[2]: Перехват исключения...
От: Аноним  
Дата: 01.02.08 23:03
Оценка:
Здравствуйте, Qbit86, Вы писали:

Q>Здравствуйте, Аноним, Вы писали:


А>>... по ссылке и по указателю. В чем разница? Как надо делать?


Q>Г. Саттер, А. Александреску «Стандарты программирования на C++», стр. 158:

Q>«73. Генерируйте исключения по значению, перехватывайте — по ссылке.»

И? Почему по ссылке а не по указателю?
Re[3]: Перехват исключения...
От: Qbit86 Кипр
Дата: 01.02.08 23:18
Оценка:
Здравствуйте, Аноним, Вы писали:

А>И? Почему по ссылке а не по указателю?


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 страница).
Глаза у меня добрые, но рубашка — смирительная!
Re: Перехват исключения...
От: Михаил Белоносов Украина  
Дата: 02.02.08 06:44
Оценка:
Здравствуйте, Аноним, Вы писали:

А>... по ссылке и по указателю. В чем разница? Как надо делать?


Перехватывать надо тип исключения, который был сгенерирован. Если объект-исключение был создан через new ("throw new X;"), то ловить его нужно по указателю на этот объект ("catch(X*){ ... }"). Если исключение было создано как автоматический объект ("throw X();"), то перехватывать его можно как по значению, так и по ссылке. Разница между этими подходами следующая. Если был выброшен динамически созданный объект, его нужно удалить через delete. При перехвате по значению возможен эффект срезки (когда в последовательности catch-обработчиков сначала перехватывается тип базового класса), и это иногда может приводить к нежелательным результатам. При перехвате по ссылке срезки не происходит, и через delete объект уничтожать тоже не надо. Поэтому перехват по ссылке считается более удобным и безопасным.
Re: Перехват исключения...
От: MasterZiv СССР  
Дата: 02.02.08 10:29
Оценка:
Аноним 515 пишет:
> ... по ссылке и по указателю. В чем разница? Как надо делать?

Правильно — выбрасывать исключения по значению, а ловить — по ссылке.
Читайте об этом у Меерса, Effective C++.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Перехват исключения...
От: MasterZiv СССР  
Дата: 02.02.08 10:32
Оценка:
Михаил Белоносов пишет:
При перехвате по значению возможен эффект
> срезки (когда в последовательности 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 объект уничтожать тоже не надо. Поэтому перехват по ссылке считается более удобным и безопасным.


Спасибо! Доходчиво. Наконец понял.
Re: Перехват исключения...
От: Кодт Россия  
Дата: 04.02.08 10:03
Оценка: 12 (1)
Здравствуйте, <Аноним>, Вы писали:

А>... по ссылке и по указателю. В чем разница? Как надо делать?


Ну, про то, что это разные типы — уже сказали.
А по сути:

Когда кидаешь значение — объект исключения размещается где-то в статическом хранилище; этим ведает рантайм.
Интересный вопрос — сколько места отводится под бросаемый объект. Можно попробовать найти предел, кидая 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, а не с фактическим!
Маленький натурный эксперимент:
#include <iostream>
#include <typeinfo>
using namespace std;

struct foo
{
    virtual ~foo() {}
} f;

struct bar : foo
{
    virtual ~bar() {}
} b;



template<class T>
void report_ref(const T& x)
{
    cout << typeid(T).name() << "& @" << &x << " is " << typeid(x).name() << endl;
}

template<class T>
void report_ptr(const T* x)
{
    cout << typeid(T).name() << "* =" << x << " is " << typeid(*x).name() << endl;
}

void catcher(void (*job)())
{
    try
    {
        job();
        cout << "not thrown" << endl;
    }
    catch(bar const& x) { report_ref(x); }
    catch(foo const& x) { report_ref(x); }
    catch(bar const* x) { report_ptr(x); }
    catch(foo const* x) { report_ptr(x); }
    catch(...)
    {
        cout << "..." << endl;
    }
}


foo const& emit_ref() { return  b; }
foo const* emit_ptr() { return &b; }

void throw_rb() { throw  b; }
void throw_pb() { throw &b; }
void throw_rf() { throw emit_ref(); }
void throw_pf() { throw emit_ptr(); }

int main()
{
    cout << "b @" << &b << " | " << "f @" << &f << endl;
    catcher(throw_rb);
    catcher(throw_pb);
    catcher(throw_rf);
    catcher(throw_pf);
}

Получаем
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)


Отсюда видно: если мы хотим устроить полноценный полиморфизм времени исполнения — то должны бросать указатели (желательно, умные — чтобы самим не возиться с памятью) и затем вручную диспетчеризовать, как душе угодно.
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.