Domanda

Nel codice seguente, l' 'ex' variabile stack-based è gettato e catturato in una funzione oltre l'ambito in cui è stata dichiarata ex. Questo sembra un po 'strano per me, dal momento che (per quanto ne so) stack-based variabili non possono essere utilizzate al di fuori del campo di applicazione in cui sono stati dichiarati (lo stack viene svolto).

void f() {
    SomeKindOfException ex(...);
    throw ex;
}

void g() {
    try {
        f();
    } catch (SomeKindOfException& ex) {
        //Handling code...
    }
}

Ho aggiunto una dichiarazione di stampa per distruttore di SomeKindOfException e si vede che l'ex viene distrutta una volta che va fuori del campo di applicazione in f (), ma poi è preso in g () e distrutto ancora una volta che va fuori di portata là pure .

Qualsiasi aiuto?

È stato utile?

Soluzione

L'oggetto eccezione viene copiato in un luogo speciale per sopravvivere alla pila di svolgimento. La ragione per cui si vedono due distruzioni è perché quando si esce f () l'eccezione originale viene distrutto e quando si esce g () la copia viene distrutta.

Altri suggerimenti

L'oggetto viene copiato in un oggetto eccezione che sopravvive pila svolgitore. Dove la memoria per quell'oggetto viene dal non è specificato. Per grande oggetto, probabilmente sarà malloc'ed, e per gli oggetti più piccoli, l'attuazione potrebbe avere un buffer pre-assegnati (posso immaginare questo potrebbe essere usato per un'eccezione bad_alloc).

Il ex di riferimento è quindi legato a quell'oggetto eccezione , che è un temporaneo (che non ha un nome).

C ++ standard 15.1 / 4:

  

La memoria per la copia temporanea del all'eccezione generata viene allocata in modo non specificato,   salvo quanto indicato in 3.7.3.1. La temporanea persiste fino a quando v'è un gestore di essere giustiziato per questo   eccezione. In particolare, se un gestore esce eseguendo un tiro; dichiarazione, che passa il controllo a un altro   gestore per la stessa eccezione, in modo che i resti temporanea. Quando l'ultimo gestore viene eseguito per la   uscite di eccezione con qualsiasi mezzo diverso buttare; l'oggetto temporaneo viene distrutto e l'attuazione   può rilasciare la memoria per l'oggetto temporaneo; tale deallocazione è fatto in un modo non specificato.   La distruzione avviene immediatamente dopo la distruzione dell'oggetto dichiarato l'eccezione dichiarazione   nel gestore.

Non c'è più niente da dire.

Quando si lancia ex esso viene copiato in una posizione speciale memoria utilizzata per gli oggetti eccezione generata. Tale copia viene effettuata dal normale costruttore di copia.

Si può vedere questo facilmente da questo esempio:

#include <iostream>

void ThrowIt();

class TestException
{
  public:
    TestException()
    {
        std::cerr<<this<<" - inside default constructor"<<std::endl;
    }

    TestException(const TestException & Right)
    {
        (void)Right;
        std::cerr<<this<<" - inside copy constructor"<<std::endl;
    }

    ~TestException()
    {
        std::cerr<<this<<" - inside destructor"<<std::endl;    
    }
};

int main()
{
    try
    {
        ThrowIt();
    }
    catch(TestException & ex)
    {
        std::cout<<"Caught exception ("<<&ex<<")"<<std::endl;
    }
    return 0;
}

void ThrowIt()
{
    TestException ex;
    throw ex;
}

Esempio di output:

matteo@teolapubuntu:~/cpp/test$ g++ -O3 -Wall -Wextra -ansi -pedantic ExceptionStack.cpp -o ExceptionStack.x
matteo@teolapubuntu:~/cpp/test$ ./ExceptionStack.x 
0xbf8e202f - inside default constructor
0x9ec0068 - inside copy constructor
0xbf8e202f - inside destructor
Caught exception (0x9ec0068)
0x9ec0068 - inside destructor

A proposito, si può vedere qui che la posizione di memoria utilizzata per l'oggetto lanciato (0x09ec0068) è decisamente lontano da quello dell'oggetto originale (0xbf8e202f): la pila, come al solito, ha grandi indirizzi, mentre il memoria utilizzata per l'oggetto lanciato è abbastanza giù nello spazio di indirizzo virtuale. Eppure, questo è un dettaglio di implementazione, dal momento che, come altre risposte hanno sottolineato, lo standard non dire nulla su dove la memoria per oggetto lanciato dovrebbe essere e come dovrebbe essere assegnato.

In aggiunta a ciò che la norma dice a 15,1 / 4 ( "Gestione delle eccezioni / un'eccezione") - che la memoria per la copia temporanea del all'eccezione generata è allocato in modo non specificato - un paio di altri pezzi di curiosità di come l'oggetto eccezione è allocato sono:

  • 3.7.3.1/4 ( "funzioni di allocazione") dello standard indica che l'oggetto eccezione non può essere allocato da un'espressione new o una chiamata a una 'funzione di allocazione globale' (es., Un operator new() sostituzione). Si noti che malloc() non è una 'funzione di allocazione globale' come definito dallo standard, così malloc() è sicuramente un'opzione per allocare l'oggetto eccezione.

  • "Quando viene generata un'eccezione, l'oggetto eccezione viene creato e un posto in generale su una sorta di stack dei dati eccezione" (Stanley Lippman, "Dentro il C ++ Object Model" - 7.2 gestione delle eccezioni)

  • Da Stroustrup "The ++ Programming Language, 3rd Edition C":. "A C implementazione ++ è richiesto di avere sufficiente memoria di riserva per essere in grado di gettare bad_alloc in caso di esaurimento della memoria Tuttavia, è possibile che gettare qualche altro eccezione causerà esaurimento della memoria "(14.4.5 esaurimento delle risorse).; e, "L'implementazione può applicare una vasta gamma di strategie per l'archiviazione e la trasmissione di eccezioni. E 'garantito, tuttavia, che la memoria è sufficiente per consentire new di gettare lo standard out-of-memory eccezione, bad_alloc" (14.3 eccezioni Catching) .

Si noti che le citazioni da Stroustrup sono pre-standard. Trovo interessante il fatto che la norma non sembra fare la garanzia che Stroustrup pensato abbastanza importante ricordare due volte.

Poiché la specifica afferma esplicitamente, che un oggetto temporaneo viene creato al posto del operando throw.

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