Pregunta

El siguiente código demuestra un problema extraño que tengo en un proyecto de Turbo C ++ Explorer. Uno de los tres objetos de pila en D :: D () no se destruye después de salir del alcance.

Esto solo ocurre si se compila en modo de lanzamiento, los auto_ptrs a_ y b_ son de diferentes tipos y la excepción lanzada no hereda de std :: exception. Parece que funciona bien en VC ++ 2005 y C ++ Builder 2009. Instalé la Actualización 2 de BDS2006, el paquete acumulativo de revisiones y la revisión 12.

¿Es mi código o el compilador? ¿Sabes de una solución? No ser capaz de usar de manera confiable auto_ptr en un proyecto VCL sería bastante inconveniente.


#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;
}


Esperado:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception


Consiguió:

C::C() 1
C::C() 2
C::C() 3
C::~C() 2
C::~C() 1
caught exception


Obtenido (con la línea '// std::auto_ptr<C> c_;' sin comentarios):

C::C() 1
C::C() 2
C::C() 3
C::~C() 1
caught exception


Editar: realizó cambios sugeridos

Editar 2:
Lo acabo de probar con C ++ Builder 2007 (11.0.2902.10471), que muestra el mismo problema. La configuración de lanzamiento funciona tan pronto como verifico & Quot; Información de depuración & Quot; cuadro en Proyecto - > Opciones - & Gt; Compilador C ++ - & Gt; Depuración Me sorprende que el ejecutable se haga más pequeño con & Quot; Información de depuración & Quot; habilitado (hasta 31.5 KB desde 39.5 KB).

Editar 3:
En Turbo C ++ Explorer (C ++ Builder 2006) (10.0.2288.42451), la configuración de la versión funciona si desactivo la casilla & Quot; Expansión de la función en línea (-vi) & Quot; cuadro en Proyecto - > Opciones - & Gt; Compilador C ++ - & Gt; Depuración Reemplazar la primera línea (#include <memory>) con el siguiente código también hace que funcione.

#pragma option push -vi-
#include <memory>
#pragma option pop 
¿Fue útil?

Solución

Esto parece ser un error del compilador. Acabo de ejecutar la misma muestra en VS2008SP1 y obtuve el resultado esperado.

Otros consejos

Para lo que sea que valga, GCC 3.4.6 hace lo esperado:

$ g++ main.cpp

$ a.out
C::C()
C::C()
C::~C()
C::~C()
caught exception

Es un error del compilador en C ++ Builder 2006. C ++ Builder 2009 lo corrige; esta es la salida que obtengo para BCC v6.1:

C::C() 1
C::C() 2
C::C() 3
C::~C() 3
C::~C() 2
C::~C() 1
caught exception

Si se lanza una excepción en un constructor de objetos, el destructor no se ejecutará.

El compilador no tiene forma de saber si el constructor se completó lo suficiente como para que el destructor se ejecute correctamente.

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

EDITAR: respondiendo al comentario a continuación ... En este caso, lo más probable es que un error del compilador agregue la regla 'no ejecutar el destructor' sin destruir incorrectamente los objetos en la pila.

¿Tal vez el flujo de Cout no está enjuagado? ¿Puedes probar con cerr en su lugar? ¿O poner directamente un punto de interrupción en el destructor y verificar si son golpeados?

Acabo de probar esto en la línea de comandos gratuita bcc5.5.1 y C ++ Builder 6 bcc5.64 y ambos funcionan como se esperaba, lo cual es una sorpresa teniendo en cuenta la edad que tienen. Luego probé esto en C ++ Builder 2007, bcc5.93 y el error está presente allí. De hecho, el código de ejemplo podría simplificarse a tipos primitivos y el error aún estaría presente:

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_;
};

¡Este ejemplo extremo termina haciendo que no se llame a ninguno de los destructores correspondientes para la clase C! Si está interesado en diagnosticar este error aún más, un truco que puede realizar es insertar un punto de interrupción de ensamblaje dentro de su 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();
}

Entonces lo dejarías correr a través del depurador. Cuando la ejecución alcance el punto de interrupción especificado, el programa se detendrá y el control se transferirá nuevamente al depurador. Luego puede pasar un paso por el ensamblaje para ver dónde se encuentra el problema.

Parece un error en el código de desenrollado de la pila de manejo de excepciones. Intente hacer una clase E simple con una instancia de ella en el constructor de D y vea si se llama.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top