Обработка исключений внутри деструктора (но не выбрасывание) - PullRequest
2 голосов
/ 28 октября 2019

Я узнал, что если выброс из программы-деструктора будет прерван, если это произойдет во время разматывания стека, потому что тогда будет распространяться более 1 исключения.

Вот пример с комментарием, который демонстрирует это:

class Foo
{
public:
    ~Foo()
    {
        ReleaseResources();
    }

private:
    int* pInt;

    void ReleaseResources()
    {
        if (!pInt)
            throw 0;
        else delete pInt;
    }
};

int main() try
{
    {
        Foo local;
        throw 1;
    } // aborting here, because now 2 exceptions are propagating!

    return 0;
}
catch (int& ex)
{
    return ex;
}

Однако у меня есть иерархия классов, где один из деструкторов вызывает функцию, которая может генерировать, и из-за этой иерархии записей это отравлено, что означает, что теперь все деструкторы помечены как noexcept(false).

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

Потому что я хочу, чтобы деструкторы были безопасными для исключенияЯ пришел к мысли, чтобы пометить их все как noexcept, но обработать возможные исключения внутри деструктора следующим образом:

Тот же пример, но переработанный так, что прерывание невозможно, а исключение деструкторов безопасно:

class Foo
{
public:
    ~Foo() noexcept
    {
        try
        {
            ReleaseResources();
        }
        catch (int&)
        {
            // handle exception here
            return;
        }
    }

private:
    int* pInt;

    void ReleaseResources()
    {
        if (!pInt)
            throw 0;
        else delete pInt;
    }
};

int main() try
{
    {
        Foo local;
        throw 1;
    } // OK, not aborting here...

    return 0;
}
catch (int& ex)
{
    return ex;
}

Вопрос в том, является ли этот нормальный подход обработкой исключений внутриrucotrs? Есть ли примеры, которые могли бы заставить этот дизайн пойти не так?

Основная цель - создать безопасные деструкторы исключений.

Также побочный вопрос, во втором примере, во время разматывания стека, еще есть 2распространяются исключения, как это не прерывается? разрешено ли только одно исключение при разматывании стека?

Ответы [ 2 ]

1 голос
/ 28 октября 2019

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

Да, вы можете избежать бросания деструкторов, подобных этому , если ваш код // handle exception here фактически обрабатывает исключение. Но на практике, если вы генерируете исключение во время уничтожения, это обычно означает, что не существует хорошего способа обработки исключения.

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

Ваше решение этой плохой ситуации (метание деструктора) работает, только когда вы на самом деле не в плохомситуация. На практике, если вы попытаетесь применить это, вы обнаружите, что не нужно ничего писать // handle exception here, кроме, возможно, предупреждения пользователя или регистрации проблемы.


, если только одно исключениеразрешено при размотке стека?

Нет такого правила. Проблема с выбрасыванием во время разматывания стека заключается в том, что из деструктора сбрасывается необработанное исключениеЕсли деструктор внутренне создает и перехватывает исключения, это не влияет на текущие раскрутки стека. std::terminate явно указывает, когда разматывание стека заканчивается завершением ( ссылка ):

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

[...]

- когда уничтожение объекта во время размотки стека заканчивается созданием исключения, или

[...]

1 голос
/ 28 октября 2019
~Foo() noexcept

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

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

Однако, лучшим решением в этом конкретном случае будет:

void ReleaseResources()
{
    delete pInt;
}

Нет необходимости бросать здесь, и это будет прощене делайте так.

Также побочный вопрос, во втором примере, во время разматывания стека все еще распространяются 2 исключения, как это не вызывается прерывание?

Потому что это разрешено.

...