Почему деструктор игнорируется в этом коде?

StackOverflow https://stackoverflow.com/questions/473720

  •  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 и посмотрите, будет ли он вызван.

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