Domanda

Il codice seguente mostra uno strano problema che ho in un progetto Explorer Turbo C ++. Uno dei tre oggetti dello stack in D :: D () non viene distrutto dopo essere uscito dall'ambito.

Ciò accade solo se compilato in modalità di rilascio, gli auto_ptrs a_ e b_ sono di tipi diversi e l'eccezione generata non eredita da std :: exception. Sembra funzionare bene in VC ++ 2005 e C ++ Builder 2009. Ho installato BDS2006 Update 2, l'aggiornamento rapido e l'aggiornamento rapido 12.

È il mio codice o il compilatore? Conosci una soluzione? Non essere in grado di utilizzare in modo affidabile auto_ptr in un progetto VCL sarebbe piuttosto scomodo.

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


Previsto:

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


Got:

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


Ottenuto (con la riga '// std::auto_ptr<C> c_;' non commentata):

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


Modifica: apportate modifiche suggerite
Modifica 2:
Ho appena provato con C ++ Builder 2007 (11.0.2902.10471), che mostra lo stesso problema. La configurazione della versione funziona non appena controllo la quot &; Informazioni di debug & Quot; casella nel progetto - > Opzioni - & Gt; Compilatore C ++ - & Gt; Debug. Mi sorprende che l'eseguibile si riduca con & Quot; Informazioni di debug & Quot; abilitato (fino a 31,5 KB da 39,5 KB).
Modifica 3:
In Turbo C ++ Explorer (C ++ Builder 2006) (10.0.2288.42451) la configurazione di rilascio funziona se deseleziono la & Quot; Espansione della funzione in linea (-vi) & Quot; casella nel progetto - > Opzioni - & Gt; Compilatore C ++ - & Gt; Debug. La sostituzione della prima riga (#include <memory>) con il seguente codice fa funzionare anche questa.

#pragma option push -vi-
#include <memory>
#pragma option pop 
È stato utile?

Soluzione

Questo sembra essere un bug del compilatore. Ho appena eseguito lo stesso campione in VS2008SP1 e ho ottenuto l'output previsto.

Altri suggerimenti

Per qualunque cosa valga, GCC 3.4.6 fa la cosa prevista:

$ g++ main.cpp

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

È un bug del compilatore in C ++ Builder 2006. C ++ Builder 2009 lo risolve; questo è l'output che ottengo per BCC v6.1:

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

Se viene generata un'eccezione in un costruttore di oggetti, il distruttore non verrà eseguito.

Il compilatore non ha modo di sapere se il costruttore è stato completato sufficientemente per consentire al distruttore di funzionare correttamente.

Vedi http://www.parashift.com/ C ++ - faq-lite / exceptions.html # faq-17,4

EDIT: rispondendo al commento qui sotto ... In questo caso, molto probabilmente è un bug del compilatore che contiene la regola "non eseguire il distruttore" con non distruggere erroneamente oggetti nello stack.

Forse il flusso di cout non è svuotato? Puoi provare con cerr invece? o mettere direttamente un breakpoint nel distruttore e verificare se vengono colpiti?

Ho appena provato questo sulla riga di comando gratuita bcc5.5.1 e C ++ Builder 6 bcc5.64 ed entrambi funzionano come previsto - il che è una sorpresa considerando quanti anni hanno. Poi ho provato questo in C ++ Builder 2007, bcc5.93 e il bug è presente lì. In effetti, il codice di esempio potrebbe essere semplificato ai tipi primitivi e il bug sarebbe ancora 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_;
};

Questo esempio estremo finisce per causare la chiamata di nessuno dei distruttori corrispondenti per la classe C! Se sei interessato a diagnosticare ulteriormente questo errore, un trucco che puoi eseguire è quello di inserire un punto di interruzione dell'assemblaggio nel tuo ctor D :: D ():

// 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();
}

Dovresti quindi lasciarlo scorrere attraverso il debugger. Quando l'esecuzione raggiunge quel punto di interruzione specificato, il programma si arresterà e il controllo tornerà al debugger. È quindi possibile passare attraverso l'assemblaggio per vedere dove si trova il problema.

Sembra un bug nel codice di svolgimento dello stack di gestione delle eccezioni. Prova a creare una semplice classe E con un'istanza di essa nel costruttore di D e vedi se viene chiamata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top