"Using namespace" nelle intestazioni c ++


119

In tutti i nostri corsi di c ++, tutti gli insegnanti mettono sempre using namespace std;subito dopo la #includes nei loro .hfile. 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, intenzionalmente o volendolo (l'inclusione dell'intestazione può essere annidata molto profondamente).

Quindi la mia domanda è doppia: ho ragione che using namespacenon dovrebbe essere usato nei file di intestazione e / o c'è un modo per annullarlo, qualcosa del tipo:

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

Un'altra domanda sulla stessa linea: un file di intestazione dovrebbe avere #includetutte le intestazioni di cui ha .cppbisogno il file corrispondente , solo quelle necessarie per le definizioni di intestazione e lasciare che il .cppfile #includeil resto, o nessuno e dichiarare tutto ciò di cui ha bisogno come extern?
Il ragionamento alla base della domanda è lo stesso di sopra: non voglio sorprese quando includo i .hfile.

Inoltre, se ho ragione, è un errore comune? Intendo nella programmazione del mondo reale e nei progetti "reali" là fuori.

Grazie.



3
come nota a margine, se si ottengono conflitti di nomi a causa di using namespaceistruzioni, è possibile utilizzare il nome completo per risolvere il problema.
Marius Bancila

Risposte:


115

NON dovresti assolutamente usarlo using namespacenelle intestazioni proprio per il motivo che dici, che può cambiare inaspettatamente il significato del codice in qualsiasi altro file che include quell'intestazione. Non c'è modo di annullare un using namespacealtro motivo per cui è così pericoloso. In genere uso solo grepo simili per assicurarmi che using namespacenon venga chiamato nelle intestazioni piuttosto che provare qualcosa di più complicato. Probabilmente anche i controllori di codice statici contrassegnano questo.

L'intestazione dovrebbe includere solo le intestazioni che deve compilare. Un modo semplice per imporlo è includere sempre l'intestazione di ciascun file sorgente come prima cosa, prima di qualsiasi altra intestazione. Quindi il file di origine non verrà compilato se l'intestazione non è autonoma. In alcuni casi, ad esempio facendo riferimento a classi di dettagli di implementazione all'interno di una libreria, è possibile utilizzare dichiarazioni anticipate anziché #includeperché si ha il pieno controllo sulla definizione di tale classe dichiarata in avanti.

Non sono sicuro che lo chiamerei comune, ma sicuramente si presenta di tanto in tanto, di solito scritto 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 poiché è relativamente semplice da risolvere.


2
siamo liberi di usare usingdichiarazioni nei nostri .cppfile? le 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators sono morte a portata di mano.
Christopher

1
e come dovremmo snellire le templatefunzioni - che dovrebbero essere nelle intestazioni? typedefs?
Christopher

1
@donlan, sembra che tu non abbia ricevuto risposta per un po 'di tempo ... Sì, puoi usare le usingistruzioni all'interno dei .cppfile senza troppe preoccupazioni perché l'ambito sarà limitato a quel file, ma non farlo mai prima di #includeun'istruzione. Per quanto riguarda le funzioni del modello definite nelle intestazioni, sfortunatamente non conosco una buona soluzione oltre alla semplice scrittura dello spazio dei nomi ... Forse potresti mettere una usingdichiarazione all'interno di un ambito separato { /* using statement in between brackets */ }, che almeno impedirebbe che sfugga al file corrente .
tjwrona1992

26

Elemento 59 in "Standard di codifica C ++: 101 regole, linee guida e best practice" di Sutter e Alexandrescu :

59. Non scrivere gli utilizzi dello spazio dei nomi in un file di intestazione o prima di un #include.

Gli spazi dei nomi usingsono per tua comodità, non per te da infliggere ad altri: non scrivere mai una usingdichiarazione o una usingdirettiva prima di una #includedirettiva.

Corollario: nei file di intestazione, non scrivere usingdirettive o usingdichiarazioni a livello di spazio dei nomi ; invece, qualifica in modo esplicito tutti i nomi.

Un file di intestazione è un ospite in uno o più file di origine. Un file di intestazione che include usingdirettive e dichiarazioni porta anche i suoi amici chiassosi.

Una using dichiarazione porta un amico. Una using direttiva porta tutti gli amici nello spazio dei nomi. L'utilizzo di da parte dei tuoi insegnanti using namespace std;è una direttiva sull'uso.

Più seriamente, abbiamo spazi dei nomi per evitare conflitti di nomi. Un file di intestazione ha lo scopo di fornire un'interfaccia. La maggior parte delle intestazioni è indipendente dal codice che può includerle, ora o in futuro. L'aggiunta di usingistruzioni per comodità interna all'interno dell'intestazione impone quei nomi convenienti a tutti i potenziali clienti di quell'intestazione. Ciò può portare a uno scontro di nomi. Ed è semplicemente maleducato.


12

È necessario fare attenzione quando si includono intestazioni all'interno di intestazioni. In progetti di grandi dimensioni, può creare una catena di dipendenze molto intricata che innesca ricostruzioni più grandi / più lunghe di quelle effettivamente necessarie. Dai un'occhiata a questo articolo e al suo seguito per saperne di più sull'importanza di una buona struttura fisica nei progetti C ++.

Dovresti includere le intestazioni all'interno di un'intestazione solo quando assolutamente necessario (ogni volta che è necessaria la definizione completa di una classe) e utilizzare la dichiarazione in avanti ovunque tu possa (quando la classe è richiesta è un puntatore o un riferimento).

Per quanto riguarda gli spazi dei nomi, tendo a utilizzare l'ambito esplicito dello spazio dei nomi nei miei file di intestazione e metto solo a using namespacenei miei file cpp.


1
come snellite la templatedichiarazione di funzione? che deve comparire nell'intestazione, no?
Christopher

6

Controlla gli standard di codifica del Goddard Space Flight Center (per C e C ++). Ciò risulta essere un po 'più difficile di quanto non lo fosse in passato - guarda le risposte aggiornate alle domande SO:

Lo standard di codifica GSFC C ++ dice:

§3.3.7 Ogni file di intestazione contiene #includei file che deve compilare, piuttosto che costringere gli utenti a utilizzare #includei file necessari. #includesdeve essere limitato a ciò di cui ha bisogno l'intestazione; altro #includesdovrebbe essere inserito nel file sorgente.

La prima delle domande con riferimenti incrociati ora include una citazione dallo standard di codifica GSFC C e la motivazione, ma la sostanza finisce per essere la stessa.


5

Hai ragione che using namespacein testa è pericoloso. Non conosco un modo per annullarlo. È facile da rilevare, ma basta cercarlo using namespacenei file di intestazione. Per quest'ultimo motivo è raro nei progetti reali. I colleghi 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 ne includi più velocemente si compila. Ciò fa risparmiare tempo a tutti. Tuttavia, se il file di intestazione presume che qualcosa debba essere incluso prima di esso, allora dovrebbe includerlo esso stesso. Altrimenti rende le intestazioni non autonome.


4

Hai ragione. E qualsiasi file dovrebbe includere solo le intestazioni necessarie per quel file. Per quanto riguarda "fare cose sbagliate è comune nei progetti del mondo reale?" - Oh si!


4

Come tutte le cose nella programmazione, il pragmatismo dovrebbe vincere sul dogmatismo, IMO.

Finché prendi 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 sono le collisioni di nomi, dopotutto, e con l'ubiquità di STL è improbabile che sia un problema.

D'altra parte, se fosse una decisione di uno sviluppatore in un singolo file di intestazione (non privato), posso vedere come genererebbe confusione tra il team e dovrebbe essere evitato.


4

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

Penso sia utile sottolineare che le usingdichiarazioni sono interessate dal campo di applicazione.

#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

Quindi effettivamente sì. Limitando la portata della usingdichiarazione, il suo effetto dura solo entro tale portata; è "annullato" quando tale ambito finisce.

Quando la usingdichiarazione viene dichiarata in un file al di fuori di qualsiasi altro ambito, ha un ambito di file e influisce su tutto ciò che si trova in quel file.

Nel caso di un file di intestazione, se la usingdichiarazione è nell'ambito del file, si estenderà all'ambito di qualsiasi file in cui è inclusa l'intestazione.


2
sembra che tu sia l'unico che ha capito la domanda reale ... tuttavia, il mio compilatore non è molto contento di me che uso la decelerazione all'interno della classe.
rustypaper

Questa risposta potrebbe essere resa ancora migliore spiegando il problema con l'idea dell'OP di come dovrebbe funzionare l'ambito (come le namespacecose di dichiarazione) rispetto a come funziona effettivamente (come una variabile). {}racchiudendolo limita il suo campo di applicazione, {}dopo che non fa nulla in merito ad esso. Questo è un modo accidentale in cui using namespaceviene applicato a livello globale.
TafT

2

Credo che tu possa usare 'using' nelle intestazioni C ++ in modo sicuro se scrivi le tue dichiarazioni in uno spazio dei nomi nidificato come questo:

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;

Questo dovrebbe includere solo le cose dichiarate in "DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED" senza gli spazi dei nomi utilizzati. L'ho testato sul compilatore mingw64.


Questa è una tecnica utile che non avevo mai visto prima; Grazie. Normalmente mi è piaciuto usare la qualificazione completa dell'ambito e inserire le usingdichiarazioni all'interno delle definizioni di funzione dove posso in modo che non inquinino gli spazi dei nomi al di fuori della funzione. Ma ora voglio usare i letterali C ++ 11 definiti dall'utente in un file di intestazione e, secondo la normale convenzione, gli operatori letterali sono protetti da uno spazio dei nomi; ma non voglio usarli in elenchi di inizializzatori di costruttori che non sono in uno scope che posso usare una usingdichiarazione non inquinante . Quindi questo è ottimo per risolvere quel problema.
Anthony Hall

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: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Almeno, questo è quello che sta succedendo per me in g ++.
Anthony Hall
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.