Поймать исключение по указателю в C++
Я обнаружил, что есть три способа поймать исключение, в чем различия?
1) вылов по стоимости;
2) поймать по ссылке;
3) поймать по указателю;
Я только знаю, что catch by value вызовет две копии объекта, catch by reference вызовет одну. Так как насчет поймать по указателю? Когда использовать catch по указателю? В дополнение к броску объекта, я могу бросить указатель на объект, как это?
class A {}
void f() {
A *p = new A();
throw p;
}
5 ответов
Рекомендуемый способ - бросить по значению и поймать по ссылке.
В вашем примере кода выдается указатель, что является плохой идеей, поскольку вам придется управлять памятью на сайте перехвата.
Если вы действительно чувствуете, что должны бросить указатель, используйте умный указатель, такой как shared_ptr
,
Во всяком случае, Херб Саттер и Алексей Александреску очень хорошо объясняют это в своей книге "Стандарты кодирования C++", которую я перефразировал.
См. Стандарты кодирования C++: бросок по значению, улов по ссылке.
Catch следует обычным правилам совместимости присваивания, то есть, если вы бросаете значение, вы можете перехватить его как значение или ссылку, но не как указатель; если вы бросаете указатель, вы можете поймать его только как указатель (или ссылку на указатель...).
Но на самом деле не имеет смысла бросать указатели, это только вызовет головную боль при управлении памятью. Таким образом, вы должны, в общем, следовать правилу throw по значению, catch по ссылке, как объяснил Грегори.
Microsoft MFC использует catch по указателю, но я думаю, что это было сделано для совместимости с компилятором, прежде чем try и catch были правильно реализованы; первоначально они использовали макросы TRY и CATCH для имитации. Каждое исключение происходит от CException, у которого есть метод, чтобы определить, должен ли объект быть удален.
Я не рекомендовал бы это для любого современного дизайна исключения. Поймать по ссылке это путь.
Хотя можно бросить практически любой объект любого типа, при этом мало что можно сделать (если вообще что-то). Динамическое распределение полезно в первую очередь, когда объект должен иметь время жизни, которое не подходит для автоматического размещения, то есть вы хотите, чтобы его время жизни не зависело от обычной области действия программы.
Однако в случае объекта исключения это не имеет особого смысла. Объект исключения обычно используется только внутри обработчика исключений, и вы явно хотите, чтобы он был уничтожен при выходе из (последнего) обработчика для этого исключения.
Существует также тот факт, что вы обычно хотите, чтобы код обработки исключений был достаточно простым. Например, если вы пытаетесь сообщить об исчерпании или повреждении свободного хранилища / кучи, попытка выделить объект исключения из этого исчерпанного / поврежденного свободного хранилища / кучи обычно не будет работать очень хорошо...
На самом деле не существует хорошего сценария для ловли / выдачи исключения по указателю. Семантика C++ допускает это, но это не очень полезно, так как большую часть времени вы будете вызывать временное исключение или строковый объект.
Однако некоторые библиотеки (Boost.Graph делает это, я полагаю) используют throw для передачи возвращаемого значения вызывающей стороне из глубоко рекурсивной функции; в такой ситуации возвращаемое значение может быть указателем, поэтому было бы целесообразно создать указатель.