Почему деструктор игнорируется в этом коде?
-
19-08-2019 - |
Вопрос
Следующий код демонстрирует странную проблему, с которой я столкнулся в проекте Turbo C ++ Explorer.Один из трех объектов стека в D::D() не уничтожается после выхода за пределы области видимости.
Это происходит только в том случае, если скомпилировано в режиме выпуска, auto_ptrs a_ и b_ имеют разные типы, и генерируемое исключение не наследуется от std::exception .Похоже, он отлично работает в VC ++ 2005 и C ++ Builder 2009.Я установил обновление BDS2006 2, накопительный пакет исправлений и исправление 12.
Это мой код или компилятор?Вы знаете, как это исправить?Невозможность надежно использовать auto_ptr в проекте VCL была бы довольно неудобной.
#include <memory>
#include <stdexcept>
#include <iostream>
typedef std::exception my_error; // will work fine if replaced with line below
//class my_error : public std::exception {};
class A {};
class B {};
class C
{
public:
C(int id) : id_(id) { std::cout << "C::C() " << id_ << std::endl; };
~C() { std::cout << "C::~C() " << id_ << std::endl; };
private:
int id_;
};
class D
{
public:
D()
{
C c1(1);
C c2(2);
C c3(3);
throw my_error();
};
private:
std::auto_ptr<A> a_;
std::auto_ptr<B> b_; // will work fine if replaced with line below
// std::auto_ptr<A> b_;
// std::auto_ptr<C> c_; // see expected output
};
#pragma argsused
int main(int argc, char* argv[])
{
try
{
D d;
}
catch (...)
{
std::cout << "caught exception" << std::endl;
}
return 0;
}
Ожидаемый:
C::C() 1 C::C() 2 C::C() 3 C::~C() 3 C::~C() 2 C::~C() 1 caught exception
Получил:
C::C() 1 C::C() 2 C::C() 3 C::~C() 2 C::~C() 1 caught exception
Получил (с помощью строки '// std::auto_ptr<C> c_;
' без комментариев):
C::C() 1 C::C() 2 C::C() 3 C::~C() 1 caught exception
Редактировать: Внесенные предлагаемые изменения
Правка 2:
Я только что протестировал это с C ++ Builder 2007 (11.0.2902.10471), который показывает ту же проблему.Конфигурация выпуска работает, как только я устанавливаю флажок "Информация об отладке" в Project -> Options -> C ++ Compiler -> Debugging.Меня удивляет, что исполняемый файл становится меньше при включенной "Отладочной информации" (до 31,5 КБ с 39,5 КБ ).
Правка 3:
В Turbo C ++ Explorer (C ++ Builder 2006) (10.0.2288.42451) конфигурация выпуска работает, если я сниму флажок "Расширение встроенной функции (-vi)" в Project -> Options -> C ++ Compiler -> Debugging.Замена первой строки (#include <memory>
) с помощью следующего кода это тоже работает.
#pragma option push -vi-
#include <memory>
#pragma option pop
Решение
Похоже, это ошибка компилятора.Я только что запустил тот же образец в VS2008SP1 и получил ожидаемый результат.
Другие советы
Чего бы это ни стоило, GCC 3.4.6 делает ожидаемое:
$ g++ main.cpp
$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception
Это ошибка компилятора в C ++ Builder 2006.C ++ Builder 2009 исправляет это;это результат, который я получаю для BCC v6.1:
C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception
Если в конструкторе объекта генерируется исключение, деструктор запускаться не будет.
Компилятор не имеет способа узнать, достаточно ли завершен конструктор для корректного запуска деструктора.
Видишь http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4
Редактировать:Отвечая на комментарий ниже...В данном случае это, скорее всего, ошибка компилятора, объединяющая правило "не запускать деструктор" с неправильным не уничтожением объектов в стеке.
Может быть, поток cout не сброшен?Можете ли вы попробовать вместо этого использовать cerr?или напрямую поставить точку останова в деструкторе и проверить, попали ли они?
Я только что протестировал это в бесплатной командной строке bcc5.5.1 и C ++ Builder 6 bcc5.64, и оба они работают как ожидалось - что удивительно, учитывая, сколько им лет.Затем я попробовал это в C ++ Builder 2007, bcc5.93, и ошибка там присутствует.Фактически, пример кода можно было бы упростить до примитивных типов, и ошибка все равно присутствовала бы:
class D
{
public:
D();
private:
std::auto_ptr<int> a_;
std::auto_ptr<short> b_;
std::auto_ptr<char> c_;
std::auto_ptr<bool> d_;
};
Этот экстремальный пример заканчивается тем, что ни один из соответствующих деструкторов не вызывается для класса C!Если вы заинтересованы в дальнейшей диагностике этой ошибки, вы можете выполнить один трюк - вставить точку останова сборки внутри вашего D::D() ctor:
// Note that D::D() ctor can't be inlined if it contains assembly
// limitation of borland compilers unfortunately
D::D()
{
__asm int 3;
C c1(1);
C c2(2);
C c3(3);
throw my_error();
}
Затем вы позволили бы ему запускаться через отладчик.Когда выполнение достигнет указанной точки останова, программа остановится, и управление будет передано обратно отладчику.Затем вы можете выполнить сборку за один шаг, чтобы увидеть, в чем заключается проблема.
Похоже на ошибку в коде размотки стека обработки исключений.Попробуйте создать простой класс E с его экземпляром в конструкторе D и посмотрите, будет ли он вызван.