Какова цель функции Try Block? [дублировать
-
27-10-2019 - |
Вопрос
Возможный дубликат:
Когда функция старается блокировать полезно?
Разница между синтаксисом Try-Catch для функции
Этот код бросает int
Исключение при создании Dog
объект внутри класса UseResources
. Анкет А int
Исключение поймано нормальным try-catch
Блок и выходы кода:
Cat()
Dog()
~Cat()
Inside handler
#include <iostream>
using namespace std;
class Cat
{
public:
Cat() { cout << "Cat()" << endl; }
~Cat() { cout << "~Cat()" << endl; }
};
class Dog
{
public:
Dog() { cout << "Dog()" << endl; throw 1; }
~Dog() { cout << "~Dog()" << endl; }
};
class UseResources
{
class Cat cat;
class Dog dog;
public:
UseResources() : cat(), dog() { cout << "UseResources()" << endl; }
~UseResources() { cout << "~UseResources()" << endl; }
};
int main()
{
try
{
UseResources ur;
}
catch( int )
{
cout << "Inside handler" << endl;
}
}
Теперь, если мы заменим определение UseResources()
конструктор с тем, который использует function try block
, как показано ниже,
UseResources() try : cat(), dog() { cout << "UseResources()" << endl; } catch(int) {}
Вывод такой же
Cat()
Dog()
~Cat()
Inside handler
т.е. с таким же конечным результатом.
Что тогда, цель function try block
?
Решение
Представьте себе, если UseResources
был определен так:
class UseResources
{
class Cat *cat;
class Dog dog;
public:
UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
~UseResources() { delete cat; cat = NULL; cout << "~UseResources()" << endl; }
};
Если Dog::Dog()
бросает, тогда cat
утечь память. По -прежнему UseResources
'Конструктор никогда завершенный, объект никогда не был полностью построен. И поэтому он не имеет своего деструктора.
Чтобы предотвратить эту утечку, вы должны использовать блок Function Try/Catch:
UseResources() try : cat(new Cat), dog() { cout << "UseResources()" << endl; } catch(...)
{
delete cat;
throw;
}
Чтобы более полно отвечать на ваш вопрос, целью блока/захвата на уровне функции в конструкторах является специально для такого рода очистки. Функциональный уровень Try/Catch Blocks не может глотать исключения (обычные могут). Если они что -то поймают, они бросят его снова, когда достигнут конца блока подъема, если вы не переодручает его явно с throw
. Анкет Вы можете превратить один тип исключения в другой, но вы не можете просто проглотить его и продолжать идти так, как этого не произошло.
Это еще одна причина, по которой значения и умные указатели должны использоваться вместо обнаженных указателей, даже в качестве членов класса. Потому что, как и в вашем случае, если у вас просто есть значения участников вместо указателей, вам не нужно это делать. Использование обнаженного указателя (или другой формы ресурса, не управляемого в объекте RAII), которая заставляет такие вещи.
Обратите внимание, что это в значительной степени единственное законное использование функций Try/Catch Blocks.
Больше причин не использовать функцию, попробуйте блоки. Приведенный выше код тонко сломан. Учти это:
class Cat
{
public:
Cat() {throw "oops";}
};
Итак, что происходит в UseResources
конструктор? Ну, выражение new Cat
бросит, очевидно. Но это значит, что cat
никогда не был инициализирован. Что обозначает delete cat
даст неопределенное поведение.
Вы можете попытаться исправить это, используя сложную лямбду вместо просто new Cat
:
UseResources() try
: cat([]() -> Cat* try{ return new Cat;}catch(...) {return nullptr;} }())
, dog()
{ cout << "UseResources()" << endl; }
catch(...)
{
delete cat;
throw;
}
Что теоретически решает проблему, но он нарушает предполагаемый инвариант UseResources
. Анкет А именно это UseResources::cat
будет всегда действительным указателем. Если это действительно инвариант UseResources
, тогда этот код потерпит неудачу, потому что он позволяет построить UseResources
Несмотря на исключение.
По сути, нет никакого способа сделать этот код безопасным, если только new Cat
является noexcept
(либо явно, либо неявно).
Напротив, это всегда работает:
class UseResources
{
unique_ptr<Cat> cat;
Dog dog;
public:
UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
~UseResources() { cout << "~UseResources()" << endl; }
};
Короче говоря, посмотрите на Try-Block на уровне функции как серьезный код запах.
Другие советы
Обычная функция, пробуя блоки, имеют относительно небольшую цель. Они почти идентичны блоку попробовать внутри тела:
int f1() try {
// body
} catch (Exc const & e) {
return -1;
}
int f2() {
try {
// body
} catch (Exc const & e) {
return -1;
}
}
Единственное отличие состоит в том, что блок функции живет в немного большей функции, в то время как вторая конструкция живет в соревнованиях по функциональному телу-в первой области видят только аргументы функций, а также локальные переменные (но но Это не влияет на две версии блоков Try).
Единственное интересное приложение входит в конструктор-Та-блок:
Foo() try : a(1,2), b(), c(true) { /* ... */ } catch(...) { }
Это единственный способ, которым можно поймать исключения из одного из инициализаторов. Вы не можете справиться Исключение, поскольку вся конструкция объекта все еще должна выйти из строя (следовательно, вы должны выйти из блока вылова за исключением, хотите ли вы или нет). Однако это является Единственный способ обработать исключения из списка инициализаторов конкретно.
Это полезно? Возможно нет. По сути, нет никакой разницы между блоком Try Constructor и следующим, более типичным рисунком «Инициализировать к нулевым и ассистентным», который сам по себе ужасен:
Foo() : p1(NULL), p2(NULL), p3(NULL) {
p1 = new Bar;
try {
p2 = new Zip;
try {
p3 = new Gulp;
} catch (...) {
delete p2;
throw;
}
} catch(...) {
delete p1;
throw;
}
}
Как вы можете видеть, у вас есть непристойный, не хватающий беспорядок. Конструктор-трибук был бы еще хуже, потому что вы даже не могли бы сказать, сколько указателей уже было назначено. Так что на самом деле это Только Полезно, если у вас есть точно два протекающие ассигнования. Обновлять: Спасибо чтению этот вопрос Я был предупрежден о том, что на самом деле Вы не можете использовать блок улова для очистки ресурсов Вообще, поскольку ссылка на объекты -члены являются неопределенным поведением. Так что [конечное обновление
Короче говоря: это бесполезно.