c++ - standard - what is using namespace std




Warum ist die Verwendung von Namespace std eine schlechte Praxis? (20)

Ich habe von anderen using namespace std dass das Schreiben using namespace std im Code falsch ist und ich stattdessen std::cout und std::cin direkt verwenden sollte.

Warum ist die using namespace std eine schlechte Praxis? Ist es ineffizient oder riskiert es mehrdeutige Variablen (Variablen, die denselben Namen wie eine Funktion im std Namespace haben)? Beeinträchtigt es die Leistung?


Verwenden Sie es nicht global

Es wird nur dann als "schlecht" betrachtet, wenn es global verwendet wird . Weil:

  • Sie stören den Namensraum, in dem Sie programmieren.
  • Leser werden Schwierigkeiten haben, zu erkennen, woher ein bestimmter Bezeichner kommt, wenn Sie viele using namespace xyz .
  • Was für andere Leser Ihres Quellcodes gilt, gilt für den häufigsten Leser noch mehr: Sie selbst. Kommen Sie in ein oder zwei Jahren wieder und schauen Sie ...
  • Wenn Sie nur über die using namespace std sprechen using namespace std Sie möglicherweise nicht über all das Bescheidene informiert - und wenn Sie ein neues #include hinzufügen oder zu einer neuen C ++ - #include wechseln, werden möglicherweise Namenskonflikte angezeigt, die Sie nicht kennen.

Sie können es lokal verwenden

Verwenden Sie es lokal (fast) frei. Dies verhindert natürlich die Wiederholung von std:: - und Wiederholung ist auch schlecht.

Ein Idiom für die lokale Verwendung

In C ++ 03 gab es einen Idiom - Boilerplate-Code - zur Implementierung einer swap Funktion für Ihre Klassen. Es wurde empfohlen, dass Sie tatsächlich einen lokalen using namespace std - oder zumindest using std::swap :

class Thing {
    int    value_;
    Child  child_;
public:
    // ...
    friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
    using namespace std;      // make `std::swap` available
    // swap all members
    swap(a.value_, b.value_); // `std::stwap(int, int)`
    swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}

Dies bewirkt die folgende Magie:

  • Der Compiler wählt std::swap für value_ , dh void std::swap(int, int) .
  • Wenn Sie einen void swap(Child&, Child&) überladen void swap(Child&, Child&) implementiert haben, wählt der Compiler diesen aus.
  • Wenn Sie diese Überladung nicht haben, verwendet der Compiler void std::swap(Child&,Child&) und versucht, diese auszutauschen.

Mit C ++ 11 gibt es keinen Grund mehr, dieses Muster zu verwenden. Die Implementierung von std::swap wurde geändert, um eine potenzielle Überlastung zu finden und sie auszuwählen.


  1. Sie müssen in der Lage sein, Code zu lesen, der von Personen geschrieben wurde, die einen anderen Stil und andere Best Practices haben.

  2. Wenn Sie nur Cout verwenden, wird niemand verwirrt. Wenn jedoch viele Namespaces herumfliegen und diese Klasse angezeigt wird und Sie nicht genau wissen, was sie tut, wird der explizite Namespace als Art Kommentar verwendet. Sie sehen auf den ersten Blick, "oh, das ist eine Dateisystemoperation" oder "das macht Netzwerksachen".


Die gleichzeitige Verwendung vieler Namespaces ist offensichtlich ein Desaster, aber die Verwendung von JUST Namespace std und nur Namespace std ist meiner Meinung nach keine große Sache, da Redefinition nur durch Ihren eigenen Code erfolgen kann.

Betrachten Sie sie einfach als reservierte Namen wie "int" oder "class" und das ist es.

Die Leute sollten aufhören, so anal zu sein. Dein Lehrer hatte die ganze Zeit Recht. Verwenden Sie einfach EINEN Namespace. Das ist der springende Punkt, wenn Namespaces an erster Stelle stehen. Sie dürfen nicht mehr als eine gleichzeitig verwenden. Es sei denn, es ist deine eigene. Eine erneute Definition wird also nicht stattfinden.


Dies hat nichts mit der Leistung zu tun. Aber bedenken Sie Folgendes: Sie verwenden zwei Bibliotheken namens Foo und Bar:

using namespace foo;
using namespace bar;

Alles funktioniert gut, Sie können Blah() von Foo und Quux() von Bar ohne Probleme Quux() . Aber eines Tages aktualisieren Sie auf eine neue Version von Foo 2.0, die jetzt eine Funktion namens Quux() bietet. Jetzt haben Sie einen Konflikt: Sowohl Foo 2.0 als auch Bar importieren Quux() in Ihren globalen Namespace. Dies erfordert einige Korrekturen, insbesondere wenn die Funktionsparameter übereinstimmen.

Wenn Sie foo::Blah() und bar::Quux() , wäre die Einführung von foo::Quux() kein Ereignis gewesen.


Erfahrene Programmierer verwenden, was ihre Probleme löst, und vermeiden, was neue Probleme verursacht, und sie vermeiden aus genau diesem Grund die Direktiven auf Header-Dateiebene.

Erfahrene Programmierer versuchen auch, die vollständige Qualifizierung der Namen in ihren Quelldateien zu vermeiden. Ein kleiner Grund dafür ist, dass es nicht elegant ist, mehr Code zu schreiben, wenn weniger Code ausreicht, es sei denn, es gibt gute Gründe . Ein Hauptgrund dafür ist das Deaktivieren der argumentabhängigen Suche (ADL).

Was sind diese guten Gründe ? Manchmal möchten Programmierer ADL explizit ausschalten, zu anderen Zeitpunkten, wenn sie disambiguieren möchten.

Also die folgenden sind in Ordnung:

  1. Gebrauchsanweisungen und Verwendungsdeklarationen auf Funktionsebene in Implementierungen von Funktionen
  2. Verwendungsdeklarationen auf Quelldateibasis in Quelldateien
  3. (Manchmal) using-Direktiven auf Quellendateienebene

Es geht darum, die Komplexität zu beherrschen. Die Verwendung des Namespaces zieht Dinge ein, die Sie nicht möchten, und macht es daher möglicherweise schwieriger zu debuggen (sage ich möglicherweise). Die Verwendung von std :: überall ist schwieriger zu lesen (mehr Text und all das).

Pferde für Kurse - verwalten Sie Ihre Komplexität so, wie Sie es am besten können und fühlen können.


Ich bin damit einverstanden, dass es nicht global verwendet werden sollte, es jedoch nicht so schlimm ist, lokal wie in einem namespace . Hier ist ein Beispiel aus "The C ++ Programming Language" :

namespace My_lib {

    using namespace His_lib; // everything from His_lib
    using namespace Her_lib; // everything from Her_lib

    using His_lib::String; // resolve potential clash in favor of His_lib
    using Her_lib::Vector; // resolve potential clash in favor of Her_lib

}

In diesem Beispiel wurden mögliche Namenskollisionen und Mehrdeutigkeiten aufgrund ihrer Komposition aufgelöst.

Dort explizit deklarierte Namen (einschließlich Namen, die mit using-Deklarationen wie His_lib::String deklariert wurden) haben Vorrang vor Namen, die in einem anderen Bereich von einer using-Direktive (unter using namespace Her_lib ) using namespace Her_lib .


Ich habe kürzlich eine Beschwerde über Visual Studio 2010 . Es stellte sich heraus, dass so ziemlich alle Quelldateien diese zwei Zeilen hatten:

using namespace std;
using namespace boost;

Viele Boost Funktionen werden in den C ++ 0x-Standard übernommen, und Visual Studio 2010 verfügt über viele C ++ 0x-Funktionen, sodass diese Programme plötzlich nicht kompiliert wurden.

Vermeiden Sie daher die using namespace X; ist eine Form der Zukunftssicherung, um sicherzustellen, dass eine Änderung der verwendeten Bibliotheken und / oder Header-Dateien ein Programm nicht beschädigt.


Ich stimme mit allem überein, was Greg geschrieben hat , aber ich möchte hinzufügen: Es kann noch schlimmer werden, als Greg gesagt hat!

Library Foo 2.0 könnte eine Funktion einführen, Quux() , die eindeutig für einige Ihrer Aufrufe von Quux() besser Quux() als der Code bar::Quux() Ihr Code seit Jahren aufgerufen hat. Dann wird Ihr Code immer noch kompiliert , aber er ruft lautlos die falsche Funktion auf und weiß, was. Das ist so schlimm wie es nur geht.

Beachten Sie, dass der std Namespace eine Unmenge von Bezeichnern enthält, von denen viele sehr häufig vorkommen (Denkliste, sort , string , iterator usw.), die sehr wahrscheinlich auch in anderem Code vorkommen.

Wenn Sie dies für unwahrscheinlich halten: Es wurde eine Frage zu gestellt, bei der dies ziemlich genau passierte (falsche Funktion wegen fehlendem std:: -Präfix genannt), etwa ein halbes Jahr, nachdem ich diese Antwort gab. Here ist ein anderes, jüngeres Beispiel für eine solche Frage. Das ist also ein echtes Problem.

Hier noch ein weiterer Datenpunkt: Vor vielen, vielen Jahren habe ich es auch als störend empfunden, alles aus der Standardbibliothek mit std:: voranstellen zu müssen. Dann arbeitete ich in einem Projekt, bei dem zu Beginn entschieden wurde, dass sowohl Richtlinien als auch Deklarationen mit Ausnahme von Funktionsbereichen verboten sind. Erraten Sie, was? Die meisten von uns brauchten einige Wochen, um sich an das Schreiben des Präfixes zu gewöhnen, und nach einigen weiteren Wochen waren sich die meisten sogar einig, dass der Code tatsächlich lesbarer wird . Dafür gibt es einen Grund: Ob Sie kürzere oder längere Prosa mögen, ist subjektiv, aber die Präfixe verleihen dem Code objektiv Klarheit. Nicht nur der Compiler, auch Sie finden leichter, auf welchen Bezeichner verwiesen wird.

In einem Jahrzehnt wuchs dieses Projekt um mehrere Millionen Codezeilen. Da diese Diskussionen immer wieder auftauchen, war ich einmal gespannt, wie oft der (erlaubte) Funktionsumfang tatsächlich im Projekt verwendet wurde. Ich grub die Quellen dafür und fand nur ein oder zwei Dutzend Orte, an denen es benutzt wurde. Für mich bedeutet dies, dass Entwickler, wenn sie einmal versucht wurden, std:: nicht so schmerzhaft finden, dass sie einmal pro 100 kLoC Anweisungen verwenden, selbst wenn sie verwendet werden dürfen.

Fazit: Das explizite Voranstellen von allem schadet nicht, ist gewöhnungsbedürftig und hat objektive Vorteile. Insbesondere macht es den Code für den Compiler und für menschliche Leser leichter zu interpretieren - und dies sollte wahrscheinlich das Hauptziel beim Schreiben von Code sein.


Kurzversion: Verwenden Sie keine globalen Deklarationen oder Anweisungen in Header-Dateien. Sie können sie gerne in Implementierungsdateien verwenden. Folgendes sagen Herb Sutter und Andrei Alexandrescu zu diesem Thema in den C ++ - Codierungsstandards.

Zusammenfassung

Namespace-Usings dienen Ihrer Bequemlichkeit und dürfen Sie nicht anderen zufügen: Schreiben Sie niemals eine using-Deklaration oder eine using-Direktive vor einer # include-Direktive.

Folgerung: Schreiben Sie in Header-Dateien keine Namespace-Ebene mit Anweisungen oder Deklarationen. explizit namespace-qualify alle namen. (Die zweite Regel folgt aus der ersten Regel, da Kopfzeilen niemals wissen können, welche anderen Kopfzeilen # enthalten sind.)

Diskussion

Kurz gesagt: Sie können und sollten Namespaces verwenden, indem Sie Deklarationen und Direktiven großzügig in Ihren Implementierungsdateien nach # include-Direktiven verwenden und sich dabei wohl fühlen. Trotz wiederholter gegenteiliger Behauptungen sind Namespaces, die Deklarationen und Anweisungen verwenden, kein Übel, und sie zerstören nicht den Zweck von Namespaces. Vielmehr machen sie Namespaces nutzbar .


Wenn Sie die rechten Header-Dateien importieren, haben Sie plötzlich Namen wie hex , left , plus oder count in Ihrem globalen Gültigkeitsbereich. Dies kann überraschend sein, wenn Sie nicht wissen, dass std:: diese Namen enthält. Wenn Sie versuchen, diese Namen auch lokal zu verwenden, kann dies zu Verwirrung führen.

Wenn sich das Standardmaterial in einem eigenen Namensraum befindet, müssen Sie sich nicht um Namenskollisionen mit Ihrem Code oder anderen Bibliotheken sorgen.


Ein Beispiel, bei dem die Verwendung des Namespaces std aufgrund der Mehrdeutigkeit von count einen Komplikationsfehler auslöst, der auch eine Funktion in der Algorithmusbibliothek ist.

#include <iostream>

using namespace std;

int count = 1;
int main() {
    cout<<count<<endl;
}

Ich stimme den anderen hier zu, möchte jedoch die Bedenken hinsichtlich der Lesbarkeit aufgreifen - Sie können all dies vermeiden, indem Sie einfach Typedefs oben in Ihrer Datei-, Funktions- oder Klassendeklaration verwenden.

Normalerweise verwende ich es in meiner Klassendeklaration, da Methoden in einer Klasse dazu tendieren, ähnliche Datentypen (die Member) zu behandeln, und ein Typedef ist eine Gelegenheit, einen Namen zuzuweisen, der im Kontext der Klasse aussagefähig ist. Dies erleichtert die Lesbarkeit der Definitionen der Klassenmethoden.

//header
class File
{
   typedef std::vector<std::string> Lines;
   Lines ReadLines();
}

und in der Umsetzung:

//cpp
Lines File::ReadLines()
{
    Lines lines;
    //get them...
    return lines;
}

im Gegensatz zu:

//cpp
vector<string> File::ReadLines()
{
    vector<string> lines;
    //get them...
    return lines;
}

oder:

//cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    //get them...
    return lines;
}

"Warum verwendet 'Namespace std;' als schlechte Praxis in C ++ betrachtet? "

Ich formuliere es anders herum: Warum wird das Tippen von 5 zusätzlichen Zeichen von manchen als umständlich angesehen?

Denken Sie zum Beispiel an das Schreiben einer numerischen Software. Warum sollte ich sogar in Betracht ziehen, meinen globalen Namespace zu verschmutzen, indem Sie "std :: vector" auf "vector" reduzieren, wenn "vector" eines der wichtigsten Konzepte der Problemdomäne ist?


Dies ist eine schlechte Praxis, die häufig als globale Namensraumverschmutzung bezeichnet wird. Es können Probleme auftreten, wenn mehrere Namespaces denselben Funktionsnamen mit Signatur haben. Dann ist es für den Compiler mehrdeutig, zu entscheiden, welcher aufgerufen werden soll. Dies kann vermieden werden, wenn Sie den Namespace mit Ihrem Funktionsaufruf wie angeben std::cout. Hoffe das hilft. :)


Ein Namespace ist ein benannter Bereich. Namensräume werden verwendet, um zusammengehörige Deklarationen zu gruppieren und separate Elemente voneinander zu trennen. Beispielsweise können zwei separat entwickelte Bibliotheken denselben Namen verwenden, um auf verschiedene Elemente zu verweisen, ein Benutzer kann jedoch beide verwenden:

namespace Mylib{
    template<class T> class Stack{ /* ... */ };
    / / ...
}
namespace Yourlib{
    class Stack{ /* ... */ };
    / / ...
}
void f(int max) {
    Mylib: :Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

Das Wiederholen eines Namensraumnamens kann sowohl für Leser als auch für Autoren eine Ablenkung sein. Folglich kann angegeben werden, dass Namen aus einem bestimmten Namespace ohne explizite Qualifizierung verfügbar sind. Zum Beispiel:

void f(int max) {
    using namespace Mylib; / / make names from Mylib accessible
    Stack<int> s1(max) ; / / use my stack
    Yourlib: :Stack s2(max) ; / / use your stack
    / / ...
}

Namespaces bieten ein leistungsfähiges Werkzeug für die Verwaltung verschiedener Bibliotheken und unterschiedlicher Code-Versionen. Sie bieten dem Programmierer insbesondere Alternativen, wie explizit auf einen nicht lokalen Namen verwiesen werden soll.

Quelle: Eine Übersicht über die Programmiersprache C ++ von Bjarne Stroustrup


Es hängt davon ab, wo es sich befindet. Wenn es sich um einen allgemeinen Header handelt, verringern Sie den Wert des Namespaces, indem Sie ihn mit dem globalen Namespace zusammenführen. Denken Sie daran, dies könnte ein guter Weg sein, um globale Module zu erstellen.


Ich denke nicht, dass dies unter allen Umständen eine schlechte Praxis ist, aber Sie müssen vorsichtig sein, wenn Sie es verwenden. Wenn Sie eine Bibliothek schreiben, sollten Sie wahrscheinlich die Bereichsauflösungsoperatoren mit dem Namespace verwenden, um zu verhindern, dass Ihre Bibliothek Köpfe mit anderen Bibliotheken verbindet. Für Code auf Anwendungsebene sehe ich nichts falsches.


Mit nicht qualifizierten importierten Bezeichnern benötigen Sie externe Suchtools wie grep , um herauszufinden, wo Bezeichner deklariert werden. Dies erschwert die Begründung der Programmkorrektheit.


Um Ihre Frage zu beantworten, betrachte ich sie praktisch so: Viele Programmierer (nicht alle) rufen den Namespace std auf. Daher sollte man sich angewöhnen, KEINE Dinge zu verwenden, die den Namen des Namensraums std beeinflussen oder verwenden. Das ist sehr viel gegeben, aber nicht so sehr im Vergleich zu der Anzahl möglicher kohärenter Wörter und Pseudonyme, die streng genommen werden können.

Ich meine wirklich ... zu sagen: "Verlassen Sie sich nicht darauf, dass diese Person anwesend ist", bedeutet, dass Sie sich darauf verlassen müssen, dass es NICHT anwesend ist. Sie werden ständig Probleme haben, Code-Schnipsel auszuleihen und sie ständig zu reparieren. Halten Sie einfach Ihre benutzerdefinierten und geliehenen Sachen in einem begrenzten Umfang, wie sie sein sollten, und sparen Sie SEHR mit Globals (ehrlich gesagt, sollten Globals fast immer ein letzter Ausweg für Zwecke des "Kompilierens jetzt, Vernunft später" sein). Ich glaube wirklich, dass dies ein schlechter Ratschlag von Ihrem Lehrer ist, da die Verwendung von std sowohl für "cout" als auch für "std :: cout" funktioniert, aber die Verwendung von std nur für "std :: cout". Sie werden nicht immer das Glück haben, Ihren eigenen Code zu schreiben.

HINWEIS: Konzentrieren Sie sich nicht zu sehr auf Effizienzprobleme, bis Sie tatsächlich etwas über die Funktionsweise von Compilern erfahren. Mit etwas Erfahrung beim Codieren müssen Sie nicht viel über sie lernen, bevor Sie erkennen, wie gut sie guten Code in etwas einfaches verallgemeinern können. Alles so einfach, als ob Sie das Ganze in C geschrieben hätten. Guter Code ist nur so komplex, wie er sein muss.





c++-faq