Вопрос

Возможный дубликат:
Когда функция старается блокировать полезно?
Разница между синтаксисом 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;
  }
}

Как вы можете видеть, у вас есть непристойный, не хватающий беспорядок. Конструктор-трибук был бы еще хуже, потому что вы даже не могли бы сказать, сколько указателей уже было назначено. Так что на самом деле это Только Полезно, если у вас есть точно два протекающие ассигнования. Обновлять: Спасибо чтению этот вопрос Я был предупрежден о том, что на самом деле Вы не можете использовать блок улова для очистки ресурсов Вообще, поскольку ссылка на объекты -члены являются неопределенным поведением. Так что [конечное обновление

Короче говоря: это бесполезно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top