2010-06-24 7 views
7

I costruttori delle classi dichiarate globalmente vengono richiamati prima che venga immesso main. Mentre questo può essere fonte di confusione per un nuovo lettore del codice perché è fatto così raramente, è necessariamente una cattiva idea?In C++, è buona forma scrivere codice che viene eseguito prima di main()?

+10

Ogni volta che usi le globali, dio uccide un gattino. – ereOn

+7

No I do ..... –

+0

Non lo fai, ma lo faccio .... –

risposta

18

Non è necessariamente una cattiva idea, ma di solito, sì.

In primo luogo, si tratta di dati globali e di solito i globali sono una cosa negativa. Più lo stato globale si ha, più diventa difficile ragionare sul proprio programma.

In secondo luogo, il C++ non garantisce l'ordine di inizializzazione degli oggetti statici definiti in diverse unità di conversione (file .cpp) - quindi se dipendono l'uno dall'altro, potrebbe essere nei guai.

+1

Se il codice influenza le variabili dichiarate in altri file, potresti avere problemi più grandi rispetto all'ordine di inizializzazione. Se accade che nessuna delle funzioni o variabili definite in un file vengano utilizzate direttamente altrove nel programma, il linker può scartare l'intera unità di compilazione incluso il codice di inizializzazione. (Questo è un problema se il file viene utilizzato tramite hooking o un registro che viene installato dalla costruzione globale.) –

+0

Inoltre, il threading di globals non è sicuro e il loro accesso è lento (anche l'accesso all'heap è più veloce). I Globali sono una cattiva idea nella vasta, vasta, vasta maggioranza dei casi. Ci sono, naturalmente, delle eccezioni. – Puppy

+0

I costruttori non dipendono dall'ordine e eseguono alcune inizializzazioni benigne. La filettatura viene controllata attentamente. – Bruce

1

Oltre ad essere di forma discutibile, non sarà trasferibile su alcune piattaforme.

1

Come si fa notare, è consentito. Nell'ultima azienda a cui ho lavorato, quando si presentava una situazione del genere, abbiamo deciso di aggiungere un commento appropriato a main() per indicare a quali variabili si applicava. Se hai una brutta situazione, prova a sfruttarla al meglio.

7

Sì, questo è male. Poiché non avrai modo di rilevare eccezioni e gestirle, verrà utilizzato il gestore predefinito. In C++ che significa chiamare terminare ...

Esempio: contenuto a.cpp

#include <stdexcept> 

int f() { throw std::runtime_error("boom"); return 42; } 
int i = f(); 

int main(int argc, char* argv[]) 
{ 
    return 0; 
} 

uscita di: g++ a.cpp && ./a.out

terminate called after throwing an instance of 'std::runtime_error' 
    what(): boom 
Aborted (core dumped) 

si può provare aggiungere un try ... catch nel vostro principale, che non aiutano .

Modifica: anche i punti Jalf sono validi. Ascolta il suo consiglio.

+0

che dire di main() prova {...} catch (...) {...}? –

+0

Non penso che questo esempio sia legale a causa di 'int i = f();'. Non puoi chiamare una funzione mentre dichiari una variabile globale per quanto ne so. –

+0

@Alexandre - stiamo parlando di codice chiamato PRIMA Avvio principale. Quindi un try/catch in main non può raggiungerlo perché il try non è stato inserito quando questo codice viene eseguito. –

0

L'utilizzo di oggetti globali/statici con distruttori e distruttori non banali è terribile. Ho visto abbastanza grandi progetti software che si sono trovati in una catastrofe a causa di un uso incontrollato di oggetti/singoletti globali/statici.

Il problema non è il fatto che si tratta del codice eseguito all'esterno dello main. È che quegli oggetti sono costruiti e distrutti in un ordine incontrollabile .

Inoltre, credo sia generalmente una cattiva pratica utilizzare comunque variabili globali (anche variabili ordinarie), con alcune eccezioni. Ogni pezzo di codice dovrebbe essere eseguito all'interno di un contesto ben definito e tutte le variabili dovrebbero appartenere ad esso.

+0

std :: cout è globale. – ChrisW

+0

@ChrisW: non vuoi distruggere std :: cout ... vero? – smerlin

+0

@smerlin - No, ma è un esempio di una variabile globale; che è costruito Meyers ha fornito un esempio (che ha detto di essere utilizzato nell'implementazione di std :: cout) su come controllare la sequenza in cui sono costruiti i globals; o meglio, come garantire che un globale sia costruito prima di usarlo, anche se sei globale e se la cosa che stai usando (ad es. std :: cout) si trova in una diversa unità di traduzione. – ChrisW

0

Non è male per niente e non è confuso. L'inizializzazione statica è una caratteristica deliberata del linguaggio. Usalo quando è necessario. Lo stesso con i globals. Usali quando è necessario. Come ogni caratteristica, sapere quando è appropriato e conoscerne i limiti è parte dell'essere un programmatore forte.

La cattiva forma è la ristrutturazione di un programma altrimenti perfettamente fine solo per evitare globals o inizializzazione statica.

+0

Questo tipo di presuppone che tu stia lavorando con un progetto già scritto; la domanda originale è davvero prescrittiva, non valutativa. –

0

Dovrò andare così lontano da dire che è una cattiva forma per i non POD, specialmente se si lavora in una squadra principalmente a causa di problemi di ordine di inizializzazione che possono facilmente sorgere da questo.

// the following invokes undefined behavior. 
static bool success=cout<<"hello world\n"; 

La gente generalmente non scrivere codice come sopra, ma prendere in considerazione se il successo è stato inizializzato per il valore di ritorno di un'altra funzione. Ora chiunque tentasse di usare cout o cerr o qualsiasi altro oggetto globale in quella funzione ha invocato lo stesso comportamento indefinito.

Per i tipi definiti dall'utente con costruttori e distruttori, considerare il metodo alternativo in cui l'accesso è inizializzazione:

static Foo& safe_static() 
{ 
    static Foo f; 
    return f; 
} 

Purtroppo questo ha anche problemi di sicurezza filo così un meccanismo di blocco di qualche tipo è richiesto per la costruzione di 'f' se si accede a safe_static contemporaneamente.

Detto questo, penso che si dovrebbe cercare di mantenere le cose semplici. Sfortunatamente quando si tratta di oggetti definiti dall'utente nell'ambito del file, è fin troppo facile imbattersi in comportamenti indefiniti. Il piccolo sforzo in più richiesto per scrivere qualcosa come safe_static sopra può prevenire un sacco di mal di testa.

Le eccezioni sono un altro punto. Se i tuoi oggetti statici escono dal loro costruttore, non hai modo di rilevare l'eccezione. Se vuoi che la tua applicazione sia veramente robusta e gestisca anche gli errori di avvio, dovrai strutturare il tuo codice attentamente (ad esempio: prova/cattura i blocchi nei costruttori per gli oggetti che crei nell'ambito del file in modo che l'eccezione non venga lanciata al di fuori del ctor ed evitare anche gli elenchi di inizializzatori che lanciano).

Se lavori in una squadra, potresti pensare a te stesso: "Oh, non sto accedendo a nessun altro globale nella mia classe, potrei anche renderlo un semplice globale con collegamento interno nell'ambito del file. " Potrebbe essere vero allora, ma prima che tu lo sappia, il tuo collega di lavoro aggiunge un'altra variabile globale e prova ad accedervi dal costruttore della tua classe. All'improvviso hai un comportamento indefinito che potrebbe non apparire nemmeno come un problema sulla piattaforma principale che stai bersagliando solo per il tuo codice in crash e fare altre cose strane quando provi a portare il tuo codice altrove.

In realtà non vale il potenziale mal di testa IMO, ed è un problema che è molto più facile da evitare che da risolvere.

Problemi correlati