Деструкторы не выполняются (без раскрутки стека) при возникновении исключения

Я обнаружил очень-очень странное поведение, которого никогда раньше не видел. Я работаю над сложным проектом VS2005 C ++.

class Tester
{
public:
    Tester()
    {
        TRACE("Construct Tester");
    }
    ~Tester()
    {
        TRACE("~Destruct Tester");
    }
};

void Thrower()
{
    Tester X;
    throw std::exception("Booom");
}

Что вы ожидаете увидеть в выводе трассировки при вызове Thrower()? Этот Тестер создается и затем разрушается при раскручивании стека, или нет?

По крайней мере, я так ожидаю, но деструктор Tester никогда не вызывается!

Невозможно !?!?!?!

Это ошибка Visual Studio?

Я много искал, но даже в Stackoverflow не нашел ответа.


person Elmue    schedule 25.01.2014    source источник


Ответы (1)


Мне потребовался целый день, чтобы выяснить, в чем дело.

Теперь я должен немного глубже объяснить, что я делаю. У меня есть код C ++, который компилируется в файл LIB. Приведенный выше код (Tester и Thrower) находится в этом простом файле LIB C ++.

И у меня есть еще один код C ++, который компилируется в управляемую DLL C ++, которая связана с этим файлом LIB. Итак, в конце оба кода находятся в одной и той же DLL. Я управлял функциями оболочки, которые вызывают код в файле LIB следующим образом:

ManagedWrapper()
{
    try
    {
        Thrower();
    }
    catch (std::exception& e)
    {
        throw new System::Exception(e.what());
    }
}

Это НЕ работает. С помощью этого кода у меня возникают утечки памяти и сетевые сокеты, которые не закрываются. Стек в Thrower не раскручивается.

Причина этого в том, что разматывание стека не происходит до достижения захвата. Но когда catch находится в другой библиотеке, чем throw, стек не разматывается. DLL не знает, как раскрутить стек в файле LIB (хотя оба, наконец, скомпилированы в одну и ту же DLL !!)

Но я нашел чрезвычайно простое решение.

В файле LIB мне пришлось добавить промежуточную функцию между ManagedWrapper () и Thrower (), как это. Код выглядит глупо, но проблему решает:

Catcher()
{
    try
    {
         Thrower();
    }
    catch(...) // unwind HERE
    {
        throw;
    }
}

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

Иногда код, который выглядит глупо, оказывается очень умным!

РЕЗЮМЕ: никогда не забывайте, что исключение всегда должно перехватывать ту же библиотеку, в которой оно было создано. Если они попадают за границу библиотеки, у вас серьезные проблемы.

person Elmue    schedule 25.01.2014
comment
Я бы хотел услышать от языкового юриста, позволяет ли это стандарт C ++. Я подозреваю, что то, что вы описываете, является ошибкой в ​​реализации. - person WilliamKF; 25.01.2014
comment
Если это была ошибка в Visual Studio, в Интернете должна быть какая-то информация. Но я ничего не нашел. Но может ты и прав. - person Elmue; 27.01.2014
comment
попробуйте код в последней версии VS, похоже, что это уже было исправлено, эта ссылка msdn показывает, почему это была ошибка: social.msdn.microsoft.com/Forums/vstudio/en-US/ - person Weipeng L; 24.09.2018