GeeksforGeeks
On Januar 16, 2022 by adminDie Anweisung using namespace std wird allgemein als schlechte Praxis angesehen. Die Alternative zu dieser Anweisung ist, den Namespace, zu dem der Bezeichner gehört, bei jeder Deklaration eines Typs mit dem Scope-Operator(::) anzugeben.
Die Anweisung erspart uns zwar die Eingabe von std::, wenn wir auf eine Klasse oder einen Typ zugreifen wollen, der im std-Namensraum definiert ist, aber sie importiert den gesamten std-Namensraum in den aktuellen Namensraum des Programms. Nehmen wir ein paar Beispiele, um zu verstehen, warum das nicht so gut ist
Angenommen, wir wollen cout aus dem std-Namensraum verwenden. Also schreiben wir
Beispiel 1:
#include <iostream>
using
namespace
std;
cout <<
" Something to Display"
;
Nun in einem späteren Stadium der Entwicklung, möchten wir eine andere Version von cout verwenden, die in einer Bibliothek namens „foo“ (zum Beispiel)
#include <foo.h>
#include <iostream>
using
namespace
std;
cout <<
" Something to display"
;
Beachten Sie, dass es eine Zweideutigkeit gibt, Auf welche Bibliothek verweist cout? Der Compiler könnte dies erkennen und das Programm nicht kompilieren. Im schlimmsten Fall kann das Programm zwar kompiliert werden, aber die falsche Funktion aufrufen, da wir nie angegeben haben, zu welchem Namensraum der Bezeichner gehört.
Namensräume wurden in C++ eingeführt, um Namenskonflikte bei Bezeichnern aufzulösen. Dadurch wurde sichergestellt, dass zwei Objekte denselben Namen haben können und dennoch unterschiedlich behandelt werden, wenn sie zu verschiedenen Namensräumen gehören. Beachten Sie, dass in diesem Beispiel genau das Gegenteil der Fall ist. Anstatt einen Namenskonflikt aufzulösen, erzeugen wir einen Namenskonflikt.
Wenn wir einen Namespace importieren, ziehen wir im Wesentlichen alle Typdefinitionen in den aktuellen Bereich. Der std-Namespace ist riesig. Er hat Hunderte von vordefinierten Bezeichnern, so dass es möglich ist, dass ein Entwickler die Tatsache übersieht, dass es eine andere Definition des von ihm beabsichtigten Objekts in der std-Bibliothek gibt. Ohne sich dessen bewusst zu sein, kann er seine eigene Implementierung spezifizieren und erwarten, dass diese in späteren Teilen des Programms verwendet wird. Somit gäbe es zwei Definitionen für denselben Typ im aktuellen Namensraum. Dies ist in C++ nicht erlaubt, und selbst wenn das Programm kompiliert wird, gibt es keine Möglichkeit zu wissen, welche Definition wo verwendet wird.
Die Lösung des Problems besteht darin, explizit anzugeben, zu welchem Namensraum unser Bezeichner gehört, indem man den Bereichsoperator (::) verwendet. Eine mögliche Lösung für das obige Beispiel kann also sein
#include <foo>
#include <iostream>
std::cout <<
"Something to display"
;
foo::cout <
"Something to display"
;
Aber jedes Mal, wenn wir std:: jedes Mal, wenn wir einen Typ definieren, ist mühsam. Außerdem wird unser Code durch die vielen Typendefinitionen unübersichtlicher und lässt sich nur schwer lesen. Betrachten wir zum Beispiel den Code zum Abrufen der aktuellen Zeit im Programm
Beispiel 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);
Der Quellcode, der mit komplizierten und langen Typdefinitionen übersät ist, ist nicht sehr einfach zu lesen. Das ist etwas, was Entwickler vermeiden wollen, da ihnen die Wartbarkeit des Codes am wichtigsten ist.
Es gibt einige Möglichkeiten, dieses Dilemma zu lösen, d.h. einen exakten Namensraum zu spezifizieren, ohne den Code mit std-Schlüsselwörtern zu übersäen.
Die Verwendung von Typedefs
Typedefs ersparen uns das Schreiben langer Typdefinitionen. In unserem Beispiel 1, könnten wir das Problem mit zwei Typedefs lösen, eines für die std-Bibliothek und eines für 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"
;
Anstatt ganze Namespaces zu importieren, einen verkürzten Namespace importieren
In Beispiel 2 hätten wir nur den chrono-Namespace unter std importieren können.
#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);
Wir können die Anweisung auch für den Import eines einzelnen Bezeichners verwenden. Um nur std::cout zu importieren, könnten wir
using std::cout;
Wenn Sie dennoch ganze Namespaces importieren, versuchen Sie, dies innerhalb von Funktionen oder begrenztem Umfang und nicht im globalen Umfang zu tun.
Verwenden Sie die Anweisung „using namespace std“ innerhalb von Funktionsdefinitionen oder Klassen-, Strukturdefinitionen. Dadurch werden die Namespace-Definitionen in einen lokalen Bereich importiert, und wir wissen zumindest, wo mögliche Fehler entstehen können, wenn sie auftreten.
#include <isotream>
using
namespace
std;
void
foo()
{
using
namespace
std;
}
Abschluss.
Wir haben alternative Methoden für den Zugriff auf einen Bezeichner aus einem Namespace diskutiert. Vermeiden Sie auf jeden Fall, ganze Namespaces in den Quellcode zu importieren.
Gute Programmierpraktiken mögen zwar einige Zeit in Anspruch nehmen, um sie zu erlernen und zu entwickeln, aber sie zahlen sich im Allgemeinen auf lange Sicht aus. Sauberen, eindeutigen und robusten, fehlerfreien Code zu schreiben, sollte das Ziel eines jeden Programmierers sein.
Schreibe einen Kommentar