Distruzione precoce di oggetti in c ++

5

Non sono sicuro che questo sia il forum giusto per questa domanda, ma proverò qui da questa domanda sul linguaggio c ++. Il problema:

Uso una variabile globale nel mio codice che è un oggetto grafico. Il fatto è che quando ho spento il programma ho provato a eliminare tutti gli oggetti creati (in una funzione chiamata close, dato che volevo finire alcuni processi anche da un'API C). Tuttavia, quando ho cancellato quell'oggetto grafico globale ho ricevuto un errore di violazione di accesso alla memoria alla fine del main. La ragione di ciò era che il distruttore veniva chiamato di nuovo quando la main terminava. Questo era qualcosa di cui non ero a conoscenza. Questo mi ha portato a questo programma completamente irrilevante (dal momento che volevo mantenere la variabile fino alla chiusura della GUI), ma per la domanda di programmazione c ++ veramente pertinente; È impossibile distruggere un oggetto c ++ globale prima della fine del main?

Tuttavia, ho letto questo

Statico: oggetti dichiarati in ambito globale o namespace (§6.3.4) e statica dichiarati in funzioni (§12.1.8) o classi (§16.2.12) vengono creati e inizializzati una sola volta (solo) e '' live '' fino alla fine del programma (§15.4.3). Tali oggetti sono chiamati oggetti statici. Un oggetto statico ha lo stesso indirizzo per tutta la durata dell'esecuzione di un programma. Gli oggetti statici possono causare seri problemi in un programma multi-thread perché sono condivisi tra tutti i thread e in genere richiedono il blocco per evitare la corsa dei dati (§5.3.1, §42.3)

Tuttavia, sebbene le versioni di questo testo appaiano ovunque su Internet, la mia domanda non è completamente risolta da questa spiegazione. Anche se può essere implicito, dal momento che le variabili globali sembrano sempre statiche, sarebbe bello avere una risposta diretta.

    
posta patrik 05.12.2014 - 17:38
fonte

2 risposte

9

However, though versions of this text appear all over on the internet my question is not completely answered by this explanation. Though it may be implied, since global variables always seem to be static it would be nice to have a straight out answer.

Se non lo hai creato in modo esplicito, non è necessario distruggerlo esplicitamente. Rimuoviamo solo alcune delle parole dalla tua citazione standard:

Static: Objects declared in global or namespace scope (§6.3.4) ... are created and initialized once (only) and ‘‘live’’ until the program terminates (§15.4.3). Such objects are called static objects.

Quindi, se dichiari un oggetto con scope globali o namespace, è statico e la sua durata è approssimativamente la durata del programma.

Per analogia, considera le variabili locali con ambito normale:

void foo() {
    std::string s;
    // code
}

ora non c'è modo di distruggere s senza uscire dal suo ambito di chiusura. Se lo si distrugge esplicitamente sul posto, il distruttore verrà nuovamente chiamato quando si esce dall'ambito.

Ora, considera che lo scope globale e dello spazio dei nomi sia lo scopo principale del tuo programma. Si entra in questo scope prima di main (che è annidato all'interno dell'ambito globale) e si esce dopo aver lasciato main . Questo ambito di livello superiore dura esattamente quanto il programma e non sei più in grado di distruggere i suoi oggetti prima che tu sia oggetto in qualsiasi altro ambito.

C'è una certa sottigliezza su esattamente quando viene chiamato il costruttore (generalmente prima degli avvii principali, con un fudge per consentire le librerie caricate dinamicamente senza dover parlare di specifiche della piattaforma), quando viene chiamato il distruttore (relativo ai gestori di uscita, diciamo ), e l'ordine relativo di questi per statica diversa (non ben definito, quindi non rendere l'inizializzazione o la distruzione globale dipendente da altri globali).

Naturalmente, se vuoi che l'oggetto sia globale per comodità, puoi comunque scegliere di effettuare chiamate esplicite di setup / cleanup invece di usare il codificatore / l'amministratore per questo.

    
risposta data 05.12.2014 - 18:04
fonte
6

Questa è una semplice regola C ++: oggetti che sono più complessi di un semplice intero, puntatore, ecc (non di un tipo nativo) con costruttori e distruttori. Quando un'istanza di tale oggetto viene dichiarata per valore all'interno di un ambito, il suo costruttore viene chiamato automaticamente. Quando l'ambito dichiarato è uscito, il distruttore viene chiamato automaticamente. Quindi prendi questo esempio:

// Constructor gets called now, which is before 'main()' 
// enters for a global object like this one.
GraphicsObject graphics;

int main()
{
    // Do stuff with 'graphics'
    // ...

} // When we get here, at the end of main, the destructor of 'graphics' is called.

Non c'è modo di cambiare questo comportamento, ma puoi aggirare il problema se hai bisogno di più controllo sulla durata di un oggetto. Un modo è usare i puntatori. Nell'esempio seguente, userò un puntatore raw per semplicità. non dovresti adottare comunque questo approccio. Preferisci sempre un puntatore intelligente per quello.

// Allocated dynamically with 'new'. Constructor still called here, before 'main()' enters. 
// Destructor will not be called until you 'delete' it yourself.
GraphicsObject * graphics = new GraphicsObject;

int main()
{
    // Do stuff with 'graphics'
    // ...

    // 'graphics' destructor only gets called now because we have explicitly
    // deleted it. If we didn't delete the dynamic object, the destructor
    // would never be called. Not deleting would qualify as a "memory leak", BTW.
    delete graphics;
}

L'uso di un puntatore , invece di una dichiarazione per valore , è il modo predefinito per estendere la durata di un oggetto oltre il suo ambito. Nel tuo caso particolare, tuttavia, sospetto che il problema possa essere risolto in modo più semplice introducendo un metodo cleanup() nel tuo oggetto grafico che chiami alla fine di main() . Assicurati che cleanup() libera tutte le risorse associate ma lascia l'oggetto in uno stato in cui il distruttore può ancora funzionare quando viene chiamato automaticamente alla fine di main() .

Inoltre, sembra che tu stia cercando di implementare il Singleton Pattern , che può essere implementato in diversi modi. Fai una ricerca su internet e potresti trovare altre configurazioni singleton che si adattano meglio alle tue esigenze.

    
risposta data 06.12.2014 - 18:36
fonte

Leggi altre domande sui tag