GeeksforGeeks
Il Gennaio 16, 2022 da adminLa dichiarazione using namespace std è generalmente considerata una cattiva pratica. L’alternativa a questa dichiarazione è di specificare lo spazio dei nomi a cui appartiene l’identificatore usando l’operatore di scope(::) ogni volta che dichiariamo un tipo.
Anche se la dichiarazione ci salva dal digitare std:: ogni volta che vogliamo accedere ad una classe o ad un tipo definito nello spazio dei nomi std, importa l’intero spazio dei nomi std nello spazio dei nomi corrente del programma. Facciamo qualche esempio per capire perché questa potrebbe non essere una buona cosa
Diciamo che vogliamo usare il cout dallo spazio dei nomi std. Quindi scriviamo
Esempio 1:
#include <iostream>
using
namespace
cout <<
" Something to Display"
;
Ora in una fase successiva di sviluppo vogliamo usare un’altra versione di cout che è implementata su misura in qualche libreria chiamata “foo” (per esempio)
#include <foo.h>
#include <iostream>
using
namespace
std;
cout <<
" Something to display"
;
Notare come ci sia un’ambiguità, a quale libreria punta cout? Il compilatore potrebbe rilevarla e non compilare il programma. Nel caso peggiore, il programma potrebbe ancora compilare ma chiamare la funzione sbagliata, poiché non abbiamo mai specificato a quale spazio dei nomi appartenesse l’identificatore.
Gli spazi dei nomi sono stati introdotti in C++ per risolvere i conflitti dei nomi degli identificatori. Questo assicurava che due oggetti potessero avere lo stesso nome e tuttavia essere trattati diversamente se appartenevano a diversi namespace. Notate come in questo esempio si è verificato l’esatto opposto. Invece di risolvere un conflitto di nome, in realtà creiamo un conflitto di nome.
Quando importiamo uno spazio dei nomi stiamo essenzialmente tirando tutte le definizioni di tipo nello scope corrente. Lo spazio dei nomi std è enorme. Ha centinaia di identificatori predefiniti, quindi è possibile che uno sviluppatore trascuri il fatto che c’è un’altra definizione del loro oggetto desiderato nella libreria std. Inconsapevole di questo, potrebbe procedere a specificare la propria implementazione e aspettarsi che venga usata nelle parti successive del programma. Così esisterebbero due definizioni per lo stesso tipo nel namespace corrente. Questo non è permesso in C++, e anche se il programma compila non c’è modo di sapere quale definizione viene usata dove.
La soluzione al problema è specificare esplicitamente a quale spazio dei nomi appartiene il nostro identificatore usando l’operatore scope (::). Così una possibile soluzione all’esempio precedente può essere
#include <foo>
#include <iostream>
std::cout <<
"Something to display"
;
foo::cout <
"Something to display"
;
Ma dover scrivere std:: ogni volta che definiamo un tipo è noioso. Rende anche il nostro codice più pesante con molte definizioni di tipo e rende difficile la lettura del codice. Considerate per esempio il codice per ottenere l’ora corrente nel programma
Esempio 2:
#include <chrono>
#include <iostream>
auto
start = std::chrono::high_performance_clock::now()
auto
stop
= std::chrono::high_peformance_clock::now();
auto
duration
= std::duration_cast<std::chrono::milliseconds>(stop - start);
Il codice sorgente che è disseminato di definizioni di tipo complicate e lunghe non è molto facile da leggere. Questo è qualcosa che gli sviluppatori cercano di evitare poiché la manutenibilità del codice è principalmente importante per loro.
Ci sono alcuni modi per risolvere questo dilemma, cioè specificare esattamente lo spazio dei nomi senza disseminare il codice di parole chiave std.
Considerare l’uso di typedef
typedef ci salva dallo scrivere lunghe definizioni di tipo. Nel nostro esempio 1, potremmo risolvere il problema usando due typedef uno per la libreria std e un altro per foo
#include <foo>
#include <iostream>
typedef
std::cout cout_std;
typedef
foo::cout cout_foo;
cout_std <<
"Something to write"
;
cout_foo <<
"Something to write"
;
Invece di importare interi spazi dei nomi, importa un namespace troncato
Nell’esempio 2 avremmo potuto importare solo il namespace chrono sotto std.
#include <chrono>
#include <iostream>
using
std::chrono;
auto
start = high_performance_clock::now();
auto
stop = high_performance_clock::now();
auto
duration duration_cast<milliseconds>(stop - start);
Possiamo anche usare la dichiarazione per importare un singolo identificatore. Per importare solo std::cout potremmo usare
using std::cout;
Se importate ancora interi spazi dei nomi, provate a farlo all’interno di funzioni o in uno scopo limitato e non nello scopo globale.
Utilizzate la dichiarazione “using namespace std” all’interno delle definizioni di funzione o di classe, definizioni di struct. Così facendo le definizioni dello spazio dei nomi vengono importate in uno scope locale, e almeno sappiamo da dove possono avere origine i possibili errori se si verificano.
#include <isotream>
using
namespace
std;
void
foo()
{
using
namespace
std;
}
Conclusione.
Abbiamo discusso metodi alternativi per accedere a un identificatore da uno spazio dei nomi. In tutti i casi evitate di importare interi namespace nel codice sorgente.
Anche se le buone pratiche di codifica possono richiedere un po’ di tempo per imparare e sviluppare, generalmente pagano nel lungo periodo. Scrivere codice pulito, non ambiguo e robusto senza errori dovrebbe essere l’intento di ogni sviluppatore di programmazione.
Lascia un commento