выдавание исключения из деструктора

Вот один из примеров, который я использую для рекурсивного поиска значения Key в любом месте в структуре документа:

db.getCollection('myCollection').find({

    "$where" : function(){

        var searchKey = 'find-this';
        var searchValue = 'please find me';

        return searchInObj(obj);

        function searchInObj(obj){                            
          for(var k in obj){       
            if(typeof obj[k] == 'object' && obj[k] !== null){
              if(searchInObj(obj[k])){
                return true;
              }
            } else {
              if(k == searchKey && obj[k] == searchValue){
                return true;
              }
            }          
          }                         
          return false;
        }       
    }    
})
245
задан Martin York 3 October 2008 в 06:53
поделиться

10 ответов

Выдача исключения из деструктора опасна.
, Если другое исключение уже распространяет приложение, завершится.

#include <iostream>

class Bad
{
    public:
        // Added the noexcept(false) so the code keeps its original meaning.
        // Post C++11 destructors are by default `noexcept(true)` and
        // this will (by default) call terminate if an exception is
        // escapes the destructor.
        //
        // But this example is designed to show that terminate is called
        // if two exceptions are propagating at the same time.
        ~Bad() noexcept(false)
        {
            throw 1;
        }
};
class Bad2
{
    public:
        ~Bad2()
        {
            throw 1;
        }
};


int main(int argc, char* argv[])
{
    try
    {
        Bad   bad;
    }
    catch(...)
    {
        std::cout << "Print This\n";
    }

    try
    {
        if (argc > 3)
        {
            Bad   bad; // This destructor will throw an exception that escapes (see above)
            throw 2;   // But having two exceptions propagating at the
                       // same time causes terminate to be called.
        }
        else
        {
            Bad2  bad; // The exception in this destructor will
                       // cause terminate to be called.
        }
    }
    catch(...)
    {
        std::cout << "Never print this\n";
    }

}

Это в основном сводится к:

Что-либо опасное (т.е. это могло выдать исключение) должно быть сделано через открытые методы (не обязательно непосредственно). Пользователь Вашего класса может тогда потенциально обработать эти ситуации при помощи открытых методов и ловящий любые потенциальные исключения.

деструктор тогда разрушит объект путем вызова этих методов (если пользователь не сделал так явно), но любой бросок исключений пойманы и отброшены (после того, как, пытаясь решить проблему).

Поэтому в действительности Вы передаете ответственность на пользователя. Если пользователь будет иметь возможность исправлять исключения, то они вручную вызовут соответствующие функции и обрабатывают любые ошибки. Если пользователь объекта не волнуется (поскольку объект будет уничтожен), тогда, деструктор оставляют заботиться о бизнесе.

пример:

станд.:: fstream

завершение () метод может потенциально выдать исключение. Вызовы деструктора близко (), если файл был открыт, но удостоверяется, что любые исключения не распространяют из деструктора.

Поэтому, если пользователь объекта файла хочет сделать специальную обработку для проблем, связанных с закрытием файла, они будут вручную звонить близко () и обрабатывать любые исключения. Если, с другой стороны, они не заботятся тогда, что деструктор оставят обработать ситуацию.

у Scott Myers есть превосходная статья о предмете в его книге "Эффективный C++"

Редактирование:

, по-видимому, также в "Более эффективном C++"
Объект 11: Препятствуйте тому, чтобы исключения оставили деструкторы

186
ответ дан Andy 4 November 2019 в 12:40
поделиться

Как дополнение к основным ответам, которые являются хорошими, всесторонними и точными, я хотел бы прокомментировать о статье, на которую Вы ссылаетесь - тот, который говорит, что "выдавание исключения в деструкторах не так плохо".

статья проводит строку, "что является альтернативами выдаванию исключения", и перечисляет некоторые проблемы с каждой из альтернатив. Сделав, таким образом, это приходит к заключению что, потому что мы не можем найти альтернативу без проблем, мы должны продолжать выдавать исключения.

проблема, то, что ни одна из проблем, которые она перечисляет с альтернативами, не в какой-либо степени так же плоха как поведение исключения, который, давайте помнить, "неопределенное поведение Вашей программы". Некоторые возражения автора включают "эстетически ужасный", и "поощряют плохой стиль". Теперь, который Вы имели бы? Программа с плохим стилем или та, которая показала неопределенное поведение?

5
ответ дан DJClayworth 4 November 2019 в 12:40
поделиться

Ваш деструктор мог бы выполняться в цепочке других деструкторов. Выдача исключения, которое не поймано Вашей непосредственной вызывающей стороной, может оставить несколько объектов в непоследовательном состоянии, таким образом вызвав еще больше проблем, тогда игнорирующих ошибку в операции очистки.

7
ответ дан Franci Penov 4 November 2019 в 12:40
поделиться

Ее опасное, но это также не имеет смысла с точки зрения понятности удобочитаемости/кода.

то, Что необходимо спросить, находится в этой ситуации

int foo()
{
   Object o;
   // As foo exits, o's destructor is called
}

, Что должно поймать исключение? Должен вызывающая сторона нечто? Или нечто должно обработать его? Почему вызывающая сторона нечто должна заботиться о некотором объекте, внутреннем к нечто? Мог бы быть способ, которым язык определяет это, чтобы иметь смысл, но его попытку быть нечитабельным и трудным понять.

, Что еще более важно, где делает память для Объектного движения? Где делает память, принадлежавший объект идет? Это все еще выделяется (якобы, потому что деструктор перестал работать)? Полагайте, что также объект был в стековое пространство , таким образом, его, очевидно, уведен независимо.

Тогда рассматривают этот случай

class Object
{ 
   Object2 obj2;
   Object3* obj3;
   virtual ~Object()
   {
       // What should happen when this fails? How would I actually destroy this?
       delete obj3;

       // obj 2 fails to destruct when it goes out of scope, now what!?!?
       // should the exception propogate? 
   } 
};

, Когда удаление сбоев obj3, как я на самом деле удаляю способом, который, как гарантируют, не перестанет работать? Его моя память черт возьми!

Теперь рассматривают в первом Объекте фрагмента кода, уходит автоматически, потому что на стеке, в то время как Object3 находится на "куче". Так как указателя на Object3 не стало, Вы - вид СОЛЬ. У Вас есть утечка памяти.

Теперь один безопасный способ сделать вещами является следующий

class Socket
{
    virtual ~Socket()
    {
      try 
      {
           Close();
      }
      catch (...) 
      {
          // Why did close fail? make sure it *really* does close here
      }
    } 

};

, Также посмотрите этот FAQ

12
ответ дан Martin York 4 November 2019 в 12:40
поделиться

Реальный вопрос спросить себя о броске от деструктора, "Что вызывающая сторона может сделать с этим?" Есть ли на самом деле что-либо полезное, которое можно сделать за исключением, которое сместило бы опасности, созданные путем броска из деструктора?

, Если я уничтожаю Foo объект, и Foo, деструктор выбрасывает исключение, что я могу обоснованно сделать с ним? Я могу зарегистрировать его, или я могу проигнорировать его. Это - все. Я не могу "зафиксировать" его, потому что эти Foo объекта уже не стало. Лучший случай, я регистрирую исключение и продолжаю, как будто ничего не произошло (или завершите программу). Это действительно стоит потенциально вызвать неопределенное поведение путем броска от деструктора?

20
ответ дан Derek Park 4 November 2019 в 12:40
поделиться

Бросок из деструктора может привести к катастрофическому отказу, потому что этот деструктор можно было бы назвать как часть "Раскручивания стека". Раскручивание стека является процедурой, которая происходит, когда исключение выдается. В этой процедуре были брошены все объекты, которые были продвинуты в стек начиная с "попытки" и до исключения, будет завершен->, их деструкторы назовут. И во время этой процедуры, не позволяется другой бросок исключения, потому что не возможно обработать два исключения за один раз, таким образом, это побудит вызов прерываться (), программа откажет, и управление возвратится к ОС.

53
ответ дан Gal Goldman 4 November 2019 в 12:40
поделиться

Все остальные объяснили, почему бросающие деструкторы ужасны..., что можно делать с этим? Если Вы делаете операцию, которая может перестать работать, создать отдельный открытый метод, который выполняет очистку и может выдать произвольные исключения. В большинстве случаев пользователи проигнорируют это. Если пользователи хотят контролировать успех/отказ очистки, они могут просто назвать явную стандартную программу очистки.

, Например:

class TempFile {
public:
    TempFile(); // throws if the file couldn't be created
    ~TempFile() throw(); // does nothing if close() was already called; never throws
    void close(); // throws if the file couldn't be deleted (e.g. file is open by another process)
    // the rest of the class omitted...
};
5
ответ дан Tom 4 November 2019 в 12:40
поделиться

Из проекта ISO для C ++ (ISO / IEC JTC 1 / SC 22 N 4411)

Поэтому деструкторам следует обычно ловит исключения и не позволяет им распространяться из деструктора.

3 Процесс вызова деструкторов для автоматических объектов, созданных на пути от блока try до throw- Выражение называется «разматывание стека». [Примечание: если деструктор, вызванный во время разматывания стека, выходит с исключение, вызывается std :: terminate (15.5.1). Таким образом, деструкторы, как правило, должны ловить исключения и не они распространяются из деструктора. - примечание конца]

12
ответ дан lothar 23 November 2019 в 03:04
поделиться

В настоящее время я следую политике (которую так многие говорят), что классы не должны активно генерировать исключения из своих деструкторов, а должны вместо этого предоставлять общедоступный метод «закрытия» для выполнения операции, которая может привести к сбою ...

. ..но я считаю, что деструкторы для классов контейнерного типа, например вектор, не должны маскировать исключения, возникающие из классов, которые они содержат. В этом случае я фактически использую метод «free / close», который вызывает себя рекурсивно. Да, сказал я рекурсивно. У этого безумия есть свой метод. Распространение исключения зависит от наличия стека: если возникает единственное исключение, то оба оставшихся деструктора все равно будут работать, а ожидающее исключение будет распространяться после возврата из подпрограммы, что прекрасно. Если возникает несколько исключений, то (в зависимости от компилятора) либо это первое исключение будет распространяться, либо программа завершится, что нормально. Если происходит так много исключений, что рекурсия переполняет стек, значит, что-то серьезно не так, и кто-то собирается об этом узнать, что тоже нормально. Лично я ошибаюсь в том, что ошибки взрываются, а не являются скрытыми, секретными и коварными.

Дело в том, что контейнер остается нейтральным, и его классы должны решать, будут ли они себя вести или неправильно вести себя в отношении выдачи исключений из своих деструкторов.

1
ответ дан 23 November 2019 в 03:04
поделиться

Q: Итак, у меня такой вопрос - если выброс из деструктора приводит к неопределенному поведению, как вы справляетесь ошибок, возникающих во время деструктора?

A: Есть несколько вариантов:

  1. Разрешить исключениям выходить из вашего деструктора, независимо от того, что происходит в другом месте. И при этом имейте в виду (или даже опасайтесь), что за ним может последовать std :: terminate.

  2. Никогда не позволяйте исключениям вытекать из вашего деструктора. Можно записать в журнал какой-нибудь большой красный плохой текст, если можно.

  3. мой любимый : Если std :: uncaught_exception возвращает false, пусть исключения будут вытекать. Если он вернет истину, то вернитесь к подходу регистрации.

Но хорошо ли бросать d'tors?

Я согласен с большинством из вышеперечисленных, что лучше избегать бросания в деструкторе, где это возможно. Но иногда лучше смириться с тем, что это может случиться, и хорошо с этим справиться. Я бы выбрал 3 выше.

Есть несколько странных случаев, когда на самом деле отличная идея выбросить из деструктора. Например, код ошибки «необходимо проверить». Это тип значения, возвращаемый функцией. Если вызывающий объект считывает / проверяет содержащийся код ошибки, возвращаемое значение уничтожается без уведомления. Но , если возвращенный код ошибки не был прочитан к тому моменту, когда возвращаемые значения выходят за пределы области видимости, он выдаст исключение из своего деструктора .

2
ответ дан 23 November 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: