2011-05-01 1 views
99

In tutti i nostri corsi C++, tutti gli insegnanti inseriscono sempre using namespace std; subito dopo lo #include s nei loro file .h. Questo mi sembra pericoloso da allora, includendo quell'intestazione in un altro programma, otterrò lo spazio dei nomi importato nel mio programma, forse senza rendermene conto, intendendolo o volendolo (l'inclusione dell'intestazione può essere molto profondamente annidata)."using namespace" nelle intestazioni C++

Quindi la mia domanda è duplice: Ho ragione che using namespace non deve essere utilizzato nei file di intestazione, e/o c'è qualche modo per annullarla, qualcosa come:

//header.h 
using namespace std { 
. 
. 
. 
} 

Ancora una domanda lungo la stessa linee: Se un file di intestazione #include ha tutte le intestazioni a cui corrisponde il file .cpp corrispondente, solo quelle necessarie per le definizioni dell'intestazione e lascia il file .cpp#include o nessuna e dichiara tutto ciò di cui ha bisogno come extern?
Il ragionamento alla base della domanda è lo stesso di sopra: non voglio sorprese quando includo i file .h.

Inoltre, se ho ragione, si tratta di un errore comune? Intendo nella programmazione del mondo reale e in progetti "reali" là fuori.

Grazie.

+3

http://stackoverflow.com/questions/1265039/using-std-namespace –

+2

come nota a margine, se si ottengono collisioni di nomi dovute alle istruzioni 'using namespace', è possibile utilizzare il nome completo per risolvere il problema. –

risposta

87

Non si dovrebbe assolutamente usare using namespace nelle intestazioni per il motivo esatto che si dice, che può cambiare inaspettatamente il significato del codice in qualsiasi altro file che include quell'intestazione. Non c'è modo di annullare un using namespace che è un'altra ragione per cui è così pericoloso. Di solito uso solo lo grep o simili per assicurarmi che lo using namespace non venga richiamato nelle intestazioni piuttosto che provare qualcosa di più complicato. Probabilmente anche i controllori di codice statici lo segnalano.

L'intestazione deve includere solo le intestazioni da compilare. Un modo semplice per far rispettare questo è sempre includere l'intestazione di ogni file sorgente come prima cosa, prima di qualsiasi altra intestazione. Quindi il file di origine non riuscirà a compilare se l'intestazione non è autonoma. In alcuni casi, ad esempio facendo riferimento alle classi di dettaglio dell'implementazione all'interno di una libreria, è possibile utilizzare le dichiarazioni di inoltro invece di #include perché si ha il pieno controllo sulla definizione di tale classe dichiarata inoltrata.

non sono sicuro lo chiamerei comune, ma dimostra decisamente all'altezza di tanto in tanto, di solito scritta da nuovi programmatori che non sono consapevoli delle conseguenze negative. In genere solo un po 'di educazione sui rischi si prende cura di eventuali problemi dal momento che è relativamente semplice da risolvere.

4

Hai ragione. E ogni file dovrebbe includere solo le intestazioni necessarie a quel file. Per quanto riguarda "sta facendo cose sbagliate comuni nei progetti del mondo reale?" - Oh si!

5

Hai ragione che using namespace nell'intestazione è pericoloso. Non so come annullare. È facile da rilevare, ma basta cercare using namespace nei file di intestazione. Per l'ultimo motivo è raro nei progetti reali. Collaboratori più esperti si lamenteranno presto se qualcuno fa qualcosa del genere.

Nei progetti reali le persone cercano di ridurre al minimo la quantità di file inclusi, perché meno si include il più veloce compila. Questo fa risparmiare tempo a tutti. Tuttavia se il file di intestazione presuppone che qualcosa debba essere incluso prima di esso, dovrebbe includerlo esso stesso. In caso contrario, le intestazioni non sono autonome.

12

È necessario fare attenzione quando si includono intestazioni all'interno delle intestazioni. Nei progetti di grandi dimensioni, può creare una catena di dipendenze molto aggrovigliata che innesca ricostruzioni più lunghe/più lunghe di quelle effettivamente necessarie. Controlla this article e its follow-up per saperne di più sull'importanza di una buona struttura fisica nei progetti C++.

È necessario includere solo intestazioni all'interno di un'intestazione quando assolutamente necessario (ogni volta che è necessaria la definizione completa di una classe) e utilizzare la dichiarazione diretta ovunque sia possibile (quando la classe è richiesta è un puntatore o un riferimento).

Come per gli spazi dei nomi, tendo a utilizzare lo scope dei nomi esplicito nei miei file di intestazione e inserisco solo uno using namespace nei miei file cpp.

+2

+1 per enfatizzare le dichiarazioni in avanti quando possibile. –

3

Come tutte le cose in programmazione, il pragmatismo dovrebbe conquistare il dogmatismo, IMO.

Fintanto che si prende la decisione a livello di progetto ("Il nostro progetto utilizza ampiamente STL, e non vogliamo dover anteporre tutto a std ::."), Non vedo il problema con esso . L'unica cosa che stai rischiando è il nome collisioni, dopo tutto, e con l'ubiquità di STL è improbabile che sia un problema.

D'altra parte, se si trattasse di una decisione da parte di uno sviluppatore in un singolo file di intestazione (non privato), posso vedere come ciò potrebbe generare confusione tra la squadra e dovrebbe essere evitato.

20

Articolo 59 a Sutter e Alexandrescu di "C++ norme di codifica: 101 regole, linee guida e best practice":

  1. Don’t write namespace usings in a header file or before an #include. 108

I titoli di tutte le linee guida sono a http://www.gotw.ca/publications/c++cs.htm, ma i dettagli sono un must-read per sviluppatori C++.

6

Controlla gli standard di codifica di Goddard Space Flight Center (per C e C++). Questo risulta essere un po 'più difficile di quanto usato per essere - vedere le risposte aggiornate alle domande in modo:

Il GSFC C++ standard di codifica dice:

§3.3.7 Each header file shall #include the files it needs to compile, rather than forcing users to #include the needed files. #includes shall be limited to what the header needs; other #includes should be placed in the source file.

la prima delle domande con riferimenti incrociati ora include una citazione dal GSFC C standard di codifica, e la logica, ma la sostanza finisce bei lo stesso.

2

io credo che si può usare 'utilizzando' nelle intestazioni di C++ in modo sicuro se si scrive le dichiarazioni in un namespace nidificato in questo modo:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED 
{ 
    /*using statements*/ 

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED 
    { 
     /*declarations*/ 
    } 
} 

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED; 

Ciò dovrebbe includere solo le cose dichiarate in 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' senza gli spazi dei nomi utilizzati . L'ho provato sul compilatore mingw64.

+0

Questa è una tecnica utile che non avevo mai visto prima; Grazie. Normalmente sono riuscito a utilizzare la qualifica dell'intero ambito e a mettere le dichiarazioni "using" all'interno delle definizioni di funzione dove posso, in modo da non inquinare spazi dei nomi al di fuori della funzione. Ma ora voglio usare i letterali definiti dall'utente C++ 11 in un file di intestazione e, secondo la consueta convenzione, gli operatori letterali sono protetti da uno spazio dei nomi; ma non voglio usarli in liste di inizializzatore del costruttore che non sono in un ambito che io possa usare una dichiarazione 'using' non inquinante. Quindi questo è ottimo per risolvere questo problema. –

+0

Anche se un effetto collaterale di questo modello è che tutte le classi dichiarate all'interno dello spazio dei nomi più interno verranno visualizzati in messaggi di errore del compilatore con il nome completo: 'errore: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED :: :: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED NomeClasse ...'. Almeno, questo è ciò che sta accadendo per me in g ++. –

2

Riguardo a "C'è un modo per annullare [una dichiarazione using]?"

Penso che sia utile sottolineare che le dichiarazioni using sono influenzate dall'ambito.

#include <vector> 

{ // begin a new scope with { 
    using namespace std; 
    vector myVector; // std::vector is used 
} // end the scope with } 

vector myOtherVector; // error vector undefined 
std::vector mySTDVector // no error std::vector is fully qualified 

Così efficacemente sì. Limitando l'ambito della dichiarazione using, il suo effetto dura solo all'interno di tale ambito; è "annullato" quando termina quello scopo.

Quando la dichiarazione using viene dichiarata in un file al di fuori di qualsiasi altro ambito, ha un ambito file e influisce su tutto in quel file.

Nel caso di un file di intestazione, se la dichiarazione using è al di file-scope questo si estendono alla portata di qualsiasi file dell'intestazione è incluso in.

+0

sembra che tu sia l'unico a capire la vera domanda ... comunque, la mia compilazione non è molto contenta di me usare all'interno della decelerazione della classe. – rustypaper