Почему деструктор игнорируется в этом коде? - PullRequest
7 голосов
/ 23 января 2009

Следующий код демонстрирует странную проблему, с которой я столкнулся в проекте Turbo C ++ Explorer. Один из трех объектов стека в D :: D () не уничтожается после выхода из области видимости.

Это происходит только в том случае, если скомпилировано в режиме выпуска, auto_ptrs a_ и b_ имеют разные типы, и выброшенное исключение не наследуется от std :: exception. Кажется, он отлично работает в VC ++ 2005 и C ++ Builder 2009. Я установил BDS2006 Update 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 

Ответы [ 7 ]

5 голосов
/ 23 января 2009

Это похоже на ошибку компилятора. Я просто запустил тот же образец в VS2008SP1 и получил ожидаемый результат.

4 голосов
/ 23 января 2009

Для чего бы то ни было, GCC 3.4.6 делает ожидаемое:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception
2 голосов
/ 05 мая 2009

Это ошибка компилятора в 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
1 голос
/ 09 декабря 2010

Я только что проверил это на бесплатной командной строке 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();
}

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

1 голос
/ 23 января 2009

Может быть, поток cout не сброшен? Можете ли вы попробовать с cerr вместо этого? или непосредственно поставить точку останова в деструкторе и проверить, ударились ли они?

1 голос
/ 23 января 2009

Если в конструкторе объекта возникло исключение, деструктор не запустится.

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

См. http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.4

РЕДАКТИРОВАТЬ: Отвечая на комментарий ниже ... В этом случае, скорее всего, это ошибка компилятора, которая смешивает правило «не запускать деструктор» с неправильно не уничтожающими объектами в стеке.

0 голосов
/ 23 января 2009

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

...