Was ist die Verschmutzung durch die Verwendung von Namespaces?

15

Ich habe mir den Google Coding Guide [hier] angesehen und sie empfehlen nicht, das using namespaceoder zu verwenden, namespace::functionwenn ich ihn nicht falsch interpretiert habe.

Gilt das auch für std? cout<<geht nicht ohne. Dieses Buch empfiehlt das gleiche. Wie gehe ich vor, wenn ich cout<<ohne using namespace std;oder std::cout<<arbeite?

Was ist der empfohlene Weg? std::cout<<? Die meisten C ++ - Lehrbücher lehren Anfänger, dass using namespace std;sie schlechte Codierungspraktiken propagieren.

Lord Loh.
quelle

Antworten:

18

Während ich den Google-Standard lese, können Sie die using namespace foo;Direktive nirgendwo verwenden. Diese Anweisung fügt alle im Namespace deklarierten Elemente ein und ist eine häufige Ursache für Kollisionen und unerwartetes Verhalten. Andere haben eine sehr verbreitete Methode zitiert: Sie haben irgendwo Ihre eigene Max- oder Min-Methode und diese kollidiert in einer src-Datei, in der jemand einen Header mit Ihrer Methode enthält und dann sagtusing namespace std;

An bestimmten Stellen ist es gestattet, eine Verwendungserklärung zu haben, die von der Form ist using ::foo::bar;

Die Leute setzen gerne Direktiven in ihren Code ein, weil dies viel Tipparbeit spart, aber mit Risiken verbunden ist. Wenn Sie eine Datei mit vielen cout-Anweisungen haben, kann ich verstehen, dass Sie std :: cout nicht hundertmal eingeben müssen, aber Sie können einfach sagen, dass Sie :: std :: cout verwenden. Ich behandle diese wie Variablendeklarationen: Bereich sie, wo sie gebraucht werden. Wenn eine Funktion in einer 10er-Datei eine Ausgabe schreiben muss, deklarieren Sie den cout-Weg nicht oben, sondern fügen Sie ihn in die Funktion ein, die die eigentliche Ausgabe ausführt.

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

Das ist ein bisschen extrem, nur ein paar Zeilen geben aus, aber Sie haben die Idee.

Eine andere Möglichkeit ist Alias ​​oder typedef, um die Eingabe zu minimieren. Ich finde std :: what nicht so schlimm, aber wir haben eine riesige Menge an Quellcode mit mehreren Dutzend Modulen und manchmal müssen wir Code wie schreiben console_gui::command_window::append("text"). Das wird nach einer Weile langweilig und verursacht viele lange Schlangen. Ich bin alles für so etwas wie

typedef console_gui::command_window cw;
cw::append("text");

solange die Aliase in einem lokalen Bereich erstellt werden und genügend Kontext vorhanden ist, um den Code lesbar zu machen.

Michael Mathews
quelle
1
Vielen Dank! Das ist sehr hilfreich. Sie haben nicht nur mit schönen Beispielen erklärt, warum es schlecht ist, sondern auch mit schönen Beispielen auf Lösungen hingewiesen. :-)
Lord Loh.
1
Übrigens: Die Verwendung std::endlfür das explizite Flush on stdout/ stderrist normalerweise überflüssig, diese Streams sind ohnehin an stdout/ gebunden stderr. Es verlangsamt sogar die Dinge ein bisschen.
Deduplizierer
8

Dies liegt daran, dass: 1) es den gesamten Zweck von Namespaces zunichte macht, nämlich die Namenskollision zu verringern; 2) Er stellt dem globalen Namespace den gesamten Namespace zur Verfügung, der mit der using-Direktive angegeben wurde.

Wenn Sie beispielsweise Ihre eigene max () -Funktion einfügen und definieren, kollidiert diese mit std :: max ().

http://en.cppreference.com/w/cpp/algorithm/max

Die Voreinstellung ist std :: member_you_wish_to_use, da explizit angegeben wird, welcher Namespace verwendet werden soll.

Apfelkuchen
quelle
Ich nehme an, dies bedeutet, dass ich std::max()mit dem Namensraumpräfix verwenden sollte. Oder irre ich mich?
Lord Loh.
3
Wenn Sie "using namespace std;" In Ihrem Code erhalten Sie Fehler, wenn Sie Ihre eigene Max-Funktion definieren (oder einen anderen Namen, der bereits im std-Namespace definiert ist)
Chewy Gumball
1
usingDies bedeutet nur, dass Sie mit Direktiven vorsichtig sein sollten, da dies in diesem Fall Ihre max () - Funktion unterbrechen würde, wenn Sie eine definiert und <algorithm> eingeschlossen hätten. Dies ist ein einfacher Fall, aber Sie wissen nie, was Sie brechen könnten. Sie müssten die gesamte Bibliothek kennen, um sicherzugehen, dass Sie sie nicht beschädigt haben, aber Sie können nicht wissen, ob Ihr Code in Zukunft beschädigt werden würde (dh Namenskollisionen).
ApplePie
6

Zitieren Sie den von Ihnen angegebenen Link:

Sie können eine using-Deklaration überall in einer .cc-Datei sowie in Funktionen, Methoden oder Klassen in .h-Dateien verwenden.

// OK in .cc Dateien.

// Muss sich in einer Funktion, Methode oder Klasse in .h-Dateien befinden.

using :: foo :: bar;

Der Google-Stil verbietet das Importieren von Namespaces im globalen Kontext, erlaubt dies jedoch in lokalen.

Überall dort, wo die Deklaration nur einen begrenzten und gut sichtbaren Teil des Codes betrifft, ist sie vollkommen akzeptabel.

Wenn Sie den globalen Kontext verschmutzen, ist nicht verwandter Code betroffen (implizit mithilfe Ihres Headers). Nichts passiert, wenn Sie dies im lokalen Kontext tun.

Basilevs
quelle
Wir haben die gleichen Standards. Einige unserer Mitarbeiter werden lokal einen langen Namespace eingeben. zB typedef foolicious :: barlicious fb; fb :: drink d;
Michael Mathews
1

sie empfehlen nicht, den using-Namespace oder den namespace: function` zu verwenden - wenn ich ihn nicht falsch interpretiert habe.

Du machtest. Die Nichtempfehlung gilt nur für die using namespaceRichtlinie (die gemeinhin als abusing namespacenicht ganz humorvoll bezeichnet wird). Es wird dringend empfohlen, den vollqualifizierten Namen einer Funktion oder eines Objekts zu verwenden, z std::cout.

H2CO3
quelle
1

Obwohl die Frage bereits nützliche Antworten hat, scheint ein Detail zu kurz zu kommen.

Die meisten Programmierer sind zunächst ein wenig verwirrt mit dem usingSchlüsselwort und den namespaceVerwendungsbeschreibungen, auch wenn sie versuchen, es durch Nachschlagen der Referenz zu lernen, weil Deklaration und Direktive etwas äquivalent sind. Beide sind relativ abstrakte lange Wörter, die mit d beginnen .

Auf Bezeichner in Namespaces kann zugegriffen werden, indem der Namespace explizit benannt wird:

myNamespace::myIdent

Das können viel mehr Schlüssel sein, die eingegeben werden müssen. Es kann aber auch die Bedeutung Ihres Codes verringern, wenn die meisten Bezeichner auf die gleiche Weise vorangestellt werden. Das usingSchlüsselwort hilft, diese Namespace-Nachteile zu vermeiden. Da es usingauf Compilerebene funktioniert (es ist kein Makro), wirkt es sich auf den gesamten Gültigkeitsbereich aus, in dem es verwendet wird. Aus diesem Grund beschränkt der Google-Stil seine Verwendung auf genau definierte Gültigkeitsbereiche, z. B. Klassen in Headerdateien oder Funktionen in CPP-Dateien.

... Natürlich gibt es einen Unterschied zwischen der Deklaration

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

und mit Direktive

using myNamespace; // make all identifiers of myNamespace directly accessible

Bei Verwendung in großen Bereichen führt Letzteres zu viel mehr Verwirrung.

Wolf
quelle
1

Bitte schön:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

Durch diese Schreibweise vermeiden wir fehleranfälliges ADL sowie die Verwendung von Direktiven und Deklarationen.

Dies soll eine sarkastische Antwort sein. :-D

Ich bin mit Herb Sutter über Google in diesem Fall. Aus C ++ - Codierungsstandards:

Sie können und sollten Namespace verwenden, indem Sie Deklarationen und Anweisungen in Ihren Implementierungsdateien großzügig nach # include-Anweisungen einfügen und sich dabei wohlfühlen. Trotz wiederholter gegenteiliger Behauptungen sind Namespaces, die Deklarationen und Anweisungen verwenden, nicht böse und setzen den Zweck von Namespaces nicht außer Kraft. Sie sind vielmehr das, was Namespaces nutzbar macht .

Sie können von potenziellen Namespace-Konflikten besessen sein, die sich wahrscheinlich nie manifestieren werden und in einem solchen astronomisch seltenen Ereignis wahrscheinlich nicht schwer zu beheben sind, indem Sie usingAnweisungen sorgfältig vermeiden und jede einzelne von Ihnen verwendete Funktion (bis auf die Operatoren) explizit mit usingDeklarationen oder angeben mach einfach weiter und fang an using namespace std. Letzteres empfehle ich aus Sicht der Produktivität.

In den meisten C ++ - Lehrbüchern wird Anfängern die Verwendung des Namespace std beigebracht. verbreiten sie schlechte Codierungspraxis?

Das Gegenteil, wenn Sie mich fragen, und ich glaube, Sutter oben stimmt zu.

Jetzt, im Laufe meiner Karriere, habe ich insgesamt 3 Namespace-Konflikte als direkte Folge von usingDirektiven in Codebasen festgestellt, die sich über Dutzende von Millionen von LOC erstrecken. In allen drei Fällen befanden sie sich jedoch in Quelldateien, die mehr als 50.000 Zeilen Legacy-Code umfassten, ursprünglich in C geschrieben und dann in C ++ bastardisiert wurden und eine umfangreiche eklektische Liste unterschiedlicher Funktionen, einschließlich Header aus einem Dutzend verschiedener Bibliotheken, aufwiesen eine epische Liste von#includes , die sich über eine Seite erstreckt. Trotz des epischen Durcheinanders waren sie nicht allzu schwierig zu beheben, da sie unter OSX (dem Betriebssystem, auf dem der Code nicht erstellt werden konnte) Buildfehler und keine Laufzeitfehler verursachten. Ordne deinen Code nicht so albtraumhaft an, und es sollte dir gut gehen.

Vermeiden Sie jedoch sowohl using Direktiven als auch Deklarationen in Header-Dateien. Das ist einfach nur zurückgeblieben. Aber für Quelldateien und insbesondere für solche, bei denen keine ganze Seite mit #includeDirektiven gefüllt ist , würde ich sagen, dass Sie nicht ins Schwitzen kommen, wenn Sie nicht für Google arbeiten.


quelle