Warum wird "Namespace std verwenden"; als schlechte Praxis angesehen?
Ich habe von anderen gesagt , dass das Schreiben using namespace std;
in Code falsch ist, und dass ich verwenden sollte std::cout
und std::cin
stattdessen direkt.
Warum wird dies using namespace std;
als schlechte Praxis angesehen? Ist es ineffizient oder besteht die Gefahr, dass mehrdeutige Variablen deklariert werden (Variablen, die denselben Namen wie eine Funktion im std
Namespace haben)? Beeinträchtigt es die Leistung?
Antworten
Dies hängt überhaupt nicht mit der Leistung zusammen. Beachten Sie jedoch Folgendes: Sie verwenden zwei Bibliotheken mit den Namen Foo und Bar:
using namespace foo;
using namespace bar;
Alles funktioniert gut und Sie können problemlos Blah()
von Foo und Quux()
Bar anrufen . Aber eines Tages aktualisieren Sie auf eine neue Version von Foo 2.0, die jetzt eine Funktion namens bietet Quux()
. Jetzt haben Sie einen Konflikt: Sowohl Foo 2.0 als auch Bar werden Quux()
in Ihren globalen Namespace importiert . Die Behebung dieses Problems erfordert einige Anstrengungen, insbesondere wenn die Funktionsparameter übereinstimmen.
Wenn Sie foo::Blah()
und verwendet hätten bar::Quux()
, foo::Quux()
wäre die Einführung von kein Ereignis gewesen.
Ich bin mit allem einverstanden, 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 für einige Ihrer Aufrufe eindeutig besser passt Quux()
als der bar::Quux()
Code, den Sie seit Jahren aufgerufen haben. Dann wird Ihr Code immer noch kompiliert , aber er ruft stillschweigend die falsche Funktion auf und macht Gott-weiß-was. Das ist ungefähr so schlimm, wie es nur geht.
Beachten Sie, dass der std
Namespace - Tonnen - Kennungen hat, von denen viele sehr gewöhnlichsten (denken list
, sort
, string
, iterator
etc.) , die sehr wahrscheinlich in anderen Code erscheinen, zu.
Wenn Sie dies für unwahrscheinlich halten: Hier auf Stack Overflow wurde eine Frage gestellt, bei der genau std::
ein halbes Jahr nach der Beantwortung dieser Frage ziemlich genau dies geschah (falsche Funktion wurde aufgrund des ausgelassenen Präfixes aufgerufen ). Hier ist ein weiteres, neueres Beispiel für eine solche Frage. Das ist also ein echtes Problem.
Hier noch ein Datenpunkt: Vor vielen, vielen Jahren fand ich es auch ärgerlich, alles aus der Standardbibliothek voranstellen zu müssen std::
. Dann habe ich in einem Projekt gearbeitet, in dem zu Beginn entschieden wurde, dass sowohl using
Direktiven als auch Deklarationen mit Ausnahme von Funktionsbereichen verboten sind. Erraten Sie, was? Die meisten von uns brauchten sehr wenige 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 dadurch besser lesbar wurde . 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, sondern auch Sie können leichter erkennen, auf welchen Bezeichner verwiesen wird.
In einem Jahrzehnt wuchs dieses Projekt auf mehrere Millionen Codezeilen. Da diese Diskussionen immer wieder auftauchen, war ich einmal gespannt, wie oft der (erlaubte) Funktionsumfang using
tatsächlich im Projekt verwendet wurde. Ich suchte nach den Quellen dafür und fand nur ein oder zwei Dutzend Stellen, an denen es verwendet wurde. Für mich bedeutet dies, dass Entwickler nach dem Versuch nicht std::
schmerzhaft genug sind, um Direktiven auch nur einmal alle 100 kLoC zu verwenden, selbst wenn sie verwendet werden durften.
Fazit: Das explizite Präfixieren von allem schadet nicht, ist sehr gewöhnungsbedürftig und hat objektive Vorteile. Insbesondere erleichtert dies die Interpretation des Codes durch den Compiler und durch menschliche Leser - und dies sollte wahrscheinlich das Hauptziel beim Schreiben von Code sein.
Das Problem beim Einfügen using namespace
der Header-Dateien Ihrer Klassen besteht darin, dass jeder, der Ihre Klassen verwenden möchte (indem er Ihre Header-Dateien einbezieht), auch diese anderen Namespaces "verwendet" (dh alles darin sieht).
Sie können jedoch auch eine using-Anweisung in Ihre (privaten) * .cpp-Dateien einfügen.
Beachten Sie, dass einige Leute mit meinem Sprichwort "Fühlen Sie sich frei" nicht einverstanden sind - denn obwohl eine using
Aussage in einer CPP-Datei besser ist als in einem Header (weil sie keine Auswirkungen auf Personen hat, die Ihre Header-Datei enthalten), denken sie, dass dies immer noch nicht der Fall ist gut (weil es je nach Code die Wartung der Klasse schwieriger machen könnte). Dieser C ++ Super-FAQ-Eintrag sagt:
Die using-Direktive existiert für älteren C ++ - Code und um den Übergang zu Namespaces zu erleichtern. Sie sollten sie jedoch wahrscheinlich nicht regelmäßig verwenden, zumindest nicht in Ihrem neuen C ++ - Code.
Die FAQ schlägt zwei Alternativen vor:
Eine using-Erklärung:
using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:";
Geben Sie einfach std :: ein
std::cout << "Values:";
Ich bin kürzlich auf eine Beschwerde über Visual Studio 2010 gestoßen . Es stellte sich heraus, dass so ziemlich alle Quelldateien diese beiden Zeilen hatten:
using namespace std;
using namespace boost;
Viele Boost- Funktionen werden in den C ++ 0x-Standard aufgenommen, und Visual Studio 2010 verfügt über viele C ++ 0x-Funktionen, sodass diese Programme plötzlich nicht mehr kompiliert wurden.
Vermeiden using namespace X;
ist daher eine Form der Zukunftssicherheit, um sicherzustellen, dass eine Änderung der verwendeten Bibliotheken und / oder Header-Dateien ein Programm nicht beschädigt.
Kurzversion: Verwenden Sie keine globalen using
Deklarationen oder Anweisungen in Header-Dateien. Fühlen Sie sich frei, sie in Implementierungsdateien zu verwenden. Hier ist, was Herb Sutter und Andrei Alexandrescu zu diesem Problem in C ++ Coding Standards zu sagen haben (Fettdruck für die Betonung liegt bei mir):
Zusammenfassung
Namespace-Verwendungen dienen Ihrer Bequemlichkeit und nicht anderen, die Sie anderen zufügen möchten: Schreiben Sie niemals eine using-Deklaration oder eine using-Direktive vor einer # include-Direktive.
Folgerung: Schreiben Sie in Header-Dateien keine Namespace-Ebene mit Direktiven oder Deklarationen. Stattdessen qualifizieren Sie alle Namen explizit für den Namespace. (Die zweite Regel folgt aus der ersten, da Header nie wissen können, welche anderen Header #includes nach ihnen erscheinen könnten.)
Diskussion
Kurz gesagt: Sie können und sollten den Namespace verwenden, indem Sie Deklarationen und Anweisungen in Ihren Implementierungsdateien nach # include-Anweisungen großzügig verwenden, und sich dabei wohl fühlen. Trotz wiederholter gegenteiliger Behauptungen ist der Namespace, der Deklarationen und Anweisungen verwendet, nicht böse und macht den Zweck von Namespaces nicht zunichte. Sie machen vielmehr Namespaces nutzbar .
Man sollte die using
Direktive nicht im globalen Bereich verwenden, insbesondere in Headern. Es gibt jedoch Situationen, in denen dies auch in einer Header-Datei angemessen ist:
template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
using namespace std; // No problem since scope is limited
return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}
Dies ist besser als die explizite Qualifizierung ( std::sin
, std::cos
...), da sie kürzer ist und mit benutzerdefinierten Gleitkommatypen (über argumentabhängige Suche (ADL)) arbeiten kann.
Verwenden Sie es nicht global
Es wird nur dann als "schlecht" angesehen, wenn es global verwendet wird . Weil:
- Sie überladen den Namespace, in dem Sie programmieren.
- Leser werden Schwierigkeiten haben zu erkennen, woher eine bestimmte Kennung kommt, wenn Sie viele verwenden
using namespace xyz
. - Was auch immer für andere Leser Ihres Quellcodes gilt, gilt umso mehr für den häufigsten Leser: Sie selbst. Komm in ein oder zwei Jahren zurück und schau mal ...
- Wenn Sie nur darüber sprechen, sind
using namespace std
Sie sich möglicherweise nicht all der Dinge bewusst, die Sie greifen - und wenn Sie eine weitere hinzufügen#include
oder zu einer neuen C ++ - Version wechseln, können Namenskonflikte auftreten, die Ihnen nicht bekannt waren.
Sie können es lokal verwenden
Gehen Sie voran und verwenden Sie es lokal (fast) frei. Dies verhindert natürlich, dass Sie sich wiederholen std::
- und Wiederholung ist auch schlecht.
Eine Redewendung für die lokale Verwendung
In C ++ 03 gab es eine Redewendung - Boilerplate-Code - zum Implementieren einer swap
Funktion für Ihre Klassen. Es wurde vorgeschlagen, dass Sie tatsächlich ein lokales verwenden 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 macht die folgende Magie:
- Der Compiler wählt das
std::swap
fürvalue_
, dhvoid std::swap(int, int)
. - Wenn Sie eine Überladung
void swap(Child&, Child&)
implementiert haben, wählt der Compiler diese aus. - Wenn Sie diese Überlastung nicht haben, verwendet der Compiler diese
void std::swap(Child&,Child&)
und versucht, sie am besten auszutauschen.
Mit C ++ 11 gibt es keinen Grund mehr, dieses Muster zu verwenden. Die Implementierung von std::swap
wurde geändert, um eine mögliche Überlastung zu finden und auszuwählen.
Wenn Sie die richtigen Header - Dateien importieren Sie plötzlich Namen wie hex
, left
, plus
oder count
in Ihrem globalen Rahmen. Dies kann überraschend sein, wenn Sie nicht wissen, dass std::
diese Namen enthalten sind. Wenn Sie auch versuchen, diese Namen lokal zu verwenden, kann dies zu Verwirrung führen.
Wenn sich alle Standardmaterialien in einem eigenen Namespace befinden, müssen Sie sich keine Gedanken über Namenskollisionen mit Ihrem Code oder anderen Bibliotheken machen.
Ein weiterer Grund ist die Überraschung.
Wenn ich sehe cout << blah
, anstatt zu std::cout << blah
denken: Was ist das cout
? Ist es die normale cout
? Ist es etwas Besonderes?
Erfahrene Programmierer verwenden alles, was ihre Probleme löst, und vermeiden alles, was neue Probleme verursacht. Aus genau diesem Grund vermeiden sie Verwendungsanweisungen auf Header-Dateiebene.
Erfahrene Programmierer versuchen auch, die vollständige Qualifizierung von 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 deaktivieren, manchmal möchten sie eindeutig unterscheiden.
Folgendes ist also in Ordnung:
- Using-Direktiven und using-Deklarationen auf Funktionsebene in den Implementierungen von Funktionen
- Verwendungsdeklarationen auf Quelldateiebene in Quelldateien
- (Manchmal) Verwendungsanweisungen auf Quelldateiebene
Ich bin damit einverstanden, dass es nicht global verwendet werden sollte, aber es ist nicht so böse, es lokal zu verwenden, wie in a 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 haben wir mögliche Namenskonflikte und Unklarheiten behoben, die sich aus ihrer Zusammensetzung ergeben.
Dort explizit deklarierte Namen (einschließlich Namen, die durch using-Deklarationen wie deklariert wurden His_lib::String
) haben Vorrang vor Namen, die durch eine using-Direktive ( using namespace Her_lib
) in einem anderen Bereich zugänglich gemacht werden .
Ich halte es auch für eine schlechte Praxis. Warum? Nur eines Tages dachte ich, dass die Funktion eines Namespace darin besteht, Dinge zu teilen, also sollte ich es nicht verderben, alles in eine globale Tasche zu werfen.
Wenn ich jedoch häufig 'cout' und 'cin' verwende, schreibe ich: using std::cout; using std::cin;
in die CPP-Datei (niemals in die Header-Datei, mit der sie sich verbreitet #include
). Ich denke, dass niemand vernünftig jemals einen Stream cout
oder nennen wird cin
. ;)
Es ist schön, Code zu sehen und zu wissen, was er tut. Wenn ich sehe, std::cout
weiß ich, dass das der cout
Stream der std
Bibliothek ist. Wenn ich sehe, cout
weiß ich es nicht. Es könnte der cout
Strom der std
Bibliothek sein. Oder es könnten int cout = 0;
zehn Zeilen höher in derselben Funktion sein. Oder eine static
Variable mit dem Namen cout
in dieser Datei. Es könnte alles sein.
Nehmen Sie jetzt eine Millionen-Zeilen-Codebasis, die nicht besonders groß ist, und suchen Sie nach einem Fehler, was bedeutet, dass Sie wissen, dass diese eine Million Zeilen eine Zeile enthält, die nicht das tut, was sie tun soll. cout << 1;
könnte einen static int
Namen lesen cout
, ihn um ein Bit nach links verschieben und das Ergebnis wegwerfen. Auf der Suche nach einem Fehler müsste ich das überprüfen. Kannst du sehen, wie ich es wirklich am liebsten sehe std::cout
?
Es ist eines dieser Dinge, die eine wirklich gute Idee zu sein scheinen, wenn Sie Lehrer sind und nie Code schreiben und pflegen mussten, um ihren Lebensunterhalt zu verdienen. Ich liebe es, Code zu sehen, wo (1) ich weiß, was er tut; und (2) ich bin zuversichtlich, dass die Person, die es schreibt, wusste, was es tut.
Es geht darum, Komplexität zu managen. Wenn Sie den Namespace verwenden, werden Dinge eingezogen, die Sie nicht möchten, und daher wird das Debuggen möglicherweise schwieriger (ich sage 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 gut Sie können und fühlen Sie sich in der Lage.
Erwägen
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
Beachten Sie, dass dies ein einfaches Beispiel ist. Wenn Sie Dateien mit 20 Includes und anderen Importen haben, müssen Sie eine Menge Abhängigkeiten durchlaufen, um das Problem herauszufinden. Das Schlimmste daran ist, dass Sie abhängig von den widersprüchlichen Definitionen in anderen Modulen nicht verwandte Fehler erhalten können.
Es ist nicht schrecklich, aber Sie sparen sich Kopfschmerzen, wenn Sie es nicht in Header-Dateien oder im globalen Namespace verwenden. Es ist wahrscheinlich in Ordnung, dies in sehr begrenzten Bereichen zu tun, aber ich hatte nie ein Problem damit, die zusätzlichen fünf Zeichen einzugeben, um zu klären, woher meine Funktionen stammen.
Ein konkretes Beispiel zur Klärung des Problems. Stellen Sie sich eine Situation, wo Sie zwei Bibliotheken haben, foo
und bar
, jede mit ihrem eigenen Namensraum:
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
Nehmen wir nun an, Sie verwenden foo
und bar
zusammen in Ihrem eigenen Programm wie folgt:
using namespace foo;
using namespace bar;
void main() {
a(42);
}
An diesem Punkt ist alles in Ordnung. Wenn Sie Ihr Programm ausführen, wird "etwas tun" ausgeführt. Aber später aktualisieren Sie bar
und sagen wir, es hat sich geändert wie folgt:
namespace bar {
void a(float) { /* Does something completely different */ }
}
An dieser Stelle wird ein Compilerfehler angezeigt:
using namespace foo;
using namespace bar;
void main() {
a(42); // error: call to 'a' is ambiguous, should be foo::a(42)
}
Sie müssen also einige Wartungsarbeiten durchführen, um zu verdeutlichen, dass "a" bedeutet foo::a
. Das ist unerwünscht, aber zum Glück ist es ziemlich einfach (fügen Sie einfach foo::
vor allen Aufrufen hinzu, a
dass der Compiler dies als mehrdeutig markiert).
Stellen Sie sich jedoch ein alternatives Szenario vor, in dem sich die Leiste geändert hat, um stattdessen so auszusehen:
namespace bar {
void a(int) { /* Does something completely different */ }
}
An diesem Punkt a(42)
bindet sich Ihr Aufruf, sich plötzlich an etwas zu binden, bar::a
anstatt foo::a
etwas zu tun, und es tut etwas völlig anderes. Keine Compilerwarnung oder so. Ihr Programm beginnt nur stillschweigend, etwas völlig anderes als zuvor zu tun.
Wenn Sie einen Namespace verwenden, riskieren Sie ein solches Szenario, weshalb es für Benutzer unangenehm ist, Namespaces zu verwenden. Je mehr Dinge sich in einem Namespace befinden, desto größer ist das Risiko von Konflikten. Daher ist es für Benutzer möglicherweise noch unangenehmer, einen Namespace zu verwenden std
(aufgrund der Anzahl der Dinge in diesem Namespace) als für andere Namespaces.
Letztendlich ist dies ein Kompromiss zwischen Beschreibbarkeit und Zuverlässigkeit / Wartbarkeit. Die Lesbarkeit kann ebenfalls berücksichtigt werden, aber ich konnte Argumente dafür in beide Richtungen sehen. Normalerweise würde ich sagen, dass Zuverlässigkeit und Wartbarkeit wichtiger sind, aber in diesem Fall zahlen Sie ständig die Kosten für die Beschreibbarkeit für eine ziemlich seltene Auswirkung auf Zuverlässigkeit / Wartbarkeit. Der "beste" Kompromiss bestimmt Ihr Projekt und Ihre Prioritäten.
Die gleichzeitige Verwendung vieler Namespaces ist natürlich ein Rezept für eine Katastrophe, aber die Verwendung von NUR Namespace std
und nur Namespace std
ist meiner Meinung nach keine so große Sache, da eine Neudefinition nur durch Ihren eigenen Code erfolgen kann ...
Betrachten Sie sie einfach als reservierte Namen wie "int" oder "class" und das war's.
Die Leute sollten aufhören, so anal zu sein. Ihr Lehrer hatte die ganze Zeit recht. Verwenden Sie einfach EINEN Namespace. Das ist der springende Punkt bei der Verwendung von Namespaces. Sie sollten nicht mehr als eine gleichzeitig verwenden. Es sei denn, es ist dein eigenes. Eine Neudefinition wird also nicht stattfinden.
Sie müssen in der Lage sein, Code zu lesen, der von Personen geschrieben wurde, die andere Meinungen zu Stil und Best Practices haben als Sie.
Wenn Sie nur verwenden
cout
, wird niemand verwirrt. Wenn jedoch viele Namespaces herumfliegen und Sie diese Klasse sehen und nicht genau wissen, was sie bewirkt, fungiert der explizite Namespace als eine Art Kommentar. Sie können auf den ersten Blick sehen, "oh, das ist eine Dateisystemoperation" oder "das macht Netzwerkkram".
Ich stimme den anderen hier zu, möchte aber auf die Bedenken hinsichtlich der Lesbarkeit eingehen. 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 in der Regel mit ähnlichen Datentypen (den Mitgliedern) umgehen und ein typedef die Möglichkeit bietet, einen Namen zuzuweisen, der im Kontext der Klasse von Bedeutung ist. Dies unterstützt tatsächlich die Lesbarkeit in den 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;
}
Ein Namespace ist ein benannter Bereich. Namespaces werden verwendet, um verwandte Deklarationen zu gruppieren und separate Elemente getrennt zu halten. Beispielsweise können zwei separat entwickelte Bibliotheken denselben Namen verwenden, um auf verschiedene Elemente zu verweisen, ein Benutzer kann jedoch weiterhin 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 Namespace-Namens kann sowohl für Leser als auch für Schriftsteller eine Ablenkung sein. Folglich kann angegeben werden, dass Namen aus einem bestimmten Namespace ohne explizite Qualifikation 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 leistungsstarkes Tool für die Verwaltung verschiedener Bibliotheken und verschiedener Codeversionen. Insbesondere bieten sie dem Programmierer Alternativen dazu, wie explizit auf einen nichtlokalen Namen verwiesen werden soll.
Quelle: Ein Überblick über die C ++ - Programmiersprache von Bjarne Stroustrup
Ein Beispiel, bei dem using namespace std
ein Kompilierungsfehler aufgrund der Mehrdeutigkeit der Zählung ausgelöst wird, was auch eine Funktion in der Algorithmusbibliothek ist.
#include <iostream>
#include <algorithm>
using namespace std;
int count = 1;
int main() {
cout << count << endl;
}
Dies verschlechtert die Leistung Ihrer Software oder Ihres Projekts nicht. Die Aufnahme des Namespace am Anfang Ihres Quellcodes ist nicht schlecht. Die Aufnahme der using namespace std
Anweisung hängt von Ihren Anforderungen und der Art und Weise ab, wie Sie die Software oder das Projekt entwickeln.
Das namespace std
enthält die C ++ - Standardfunktionen und -variablen. Dieser Namespace ist nützlich, wenn Sie häufig die C ++ - Standardfunktionen verwenden.
Wie auf dieser Seite erwähnt :
Die Anweisung mit dem Namespace std wird im Allgemeinen als schlechte Praxis angesehen. Die Alternative zu dieser Anweisung besteht darin, den Namespace, zu dem der Bezeichner gehört, mit dem Bereichsoperator (: :) jedes Mal anzugeben, wenn wir einen Typ deklarieren.
Und siehe diese Meinung :
Es ist kein Problem, "using namespace std" in Ihrer Quelldatei zu verwenden, wenn Sie den Namespace stark nutzen und sicher wissen, dass nichts kollidieren wird.
Einige Leute hatten gesagt, dass es eine schlechte Praxis ist, die using namespace std
in Ihre Quelldateien aufzunehmen, da Sie von diesem Namespace aus alle Funktionen und Variablen aufrufen. Wenn Sie eine neue Funktion mit demselben Namen wie eine andere in der enthaltene Funktion definieren namespace std
möchten, überladen Sie die Funktion und es können Probleme beim Kompilieren oder Ausführen auftreten. Es wird nicht wie erwartet kompiliert oder ausgeführt.
Wie auf dieser Seite erwähnt :
Die Anweisung erspart uns zwar die Eingabe von std ::, wenn wir auf eine im std-Namespace definierte Klasse oder einen Typ zugreifen möchten, importiert jedoch den gesamten std-Namespace in den aktuellen Namespace des Programms. Nehmen wir ein paar Beispiele, um zu verstehen, warum dies möglicherweise nicht so gut ist
...
Jetzt, zu einem späteren Zeitpunkt der Entwicklung, möchten wir eine andere Version von cout verwenden, die benutzerdefiniert in einer Bibliothek namens "foo" implementiert ist (zum Beispiel).
...
Beachten Sie, wie es eine Mehrdeutigkeit gibt, auf welche Bibliothek cout verweist? Der Compiler erkennt dies möglicherweise und kompiliert das Programm nicht. Im schlimmsten Fall kann das Programm zwar noch kompilieren, aber die falsche Funktion aufrufen, da wir nie angegeben haben, zu welchem Namespace der Bezeichner gehört.
Ich denke nicht, dass es unter allen Umständen unbedingt 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 mit anderen Bibliotheken in Kontakt kommt. Für Code auf Anwendungsebene sehe ich nichts Falsches daran.
Ich stimme anderen zu - es fragt nach Namenskonflikten, Mehrdeutigkeiten und dann ist die Tatsache, dass es weniger explizit ist. Während ich die Verwendung von sehen kann using
, ist es meine persönliche Präferenz, sie einzuschränken. Ich würde auch stark darüber nachdenken, worauf einige andere hingewiesen haben:
Wenn Sie einen Funktionsnamen suchen möchten, der möglicherweise ein ziemlich gebräuchlicher Name ist, ihn aber nur im std
Namespace finden möchten (oder umgekehrt - Sie möchten alle Aufrufe ändern, die sich nicht im Namespace std
, Namespace X
usw. befinden), Wie schlagen Sie das vor?
Sie könnten ein Programm schreiben, um dies zu tun, aber wäre es nicht besser, Zeit damit zu verbringen, an Ihrem Projekt selbst zu arbeiten, als ein Programm zu schreiben, um Ihr Projekt zu warten?
Persönlich stört mich das std::
Präfix eigentlich nicht . Ich mag das Aussehen mehr als es nicht zu haben. Ich weiß nicht, ob das daran liegt, dass es explizit ist und zu mir sagt "das ist nicht mein Code ... ich verwende die Standardbibliothek" oder ob es etwas anderes ist, aber ich denke, es sieht besser aus. Dies könnte seltsam sein, da ich erst kürzlich in C ++ eingestiegen bin (C und andere Sprachen werden viel länger verwendet und immer noch verwendet und C ist meine Lieblingssprache aller Zeiten, direkt über der Assembly).
Es gibt noch eine andere Sache, obwohl sie etwas mit dem oben Gesagten zu tun hat und worauf andere hinweisen. Obwohl dies eine schlechte Praxis sein kann, reserviere ich manchmal std::name
die Standardbibliotheksversion und den Namen für die programmspezifische Implementierung. Ja, in der Tat könnte dies Sie beißen und Sie hart beißen, aber es kommt darauf an, dass ich dieses Projekt von Grund auf neu gestartet habe und ich der einzige Programmierer dafür bin. Beispiel: Ich überlade std::string
und nenne es string
. Ich habe hilfreiche Ergänzungen. Ich habe es teilweise aufgrund meiner Tendenz zu Kleinbuchstaben unter C und Unix (+ Linux) gemacht.
Außerdem können Sie Namespace-Aliase haben. Hier ist ein Beispiel dafür, wo es nützlich ist, auf das möglicherweise nicht Bezug genommen wurde. Ich benutze den C ++ 11 Standard und speziell mit libstdc ++. Nun, es hat keine vollständige std::regex
Unterstützung. Sicher, es wird kompiliert, aber es löst eine Ausnahme aus, da es sich um einen Fehler des Programmierers handelt. Aber es ist mangelnde Umsetzung.
So habe ich es gelöst. Installieren Sie den regulären Ausdruck von Boost und verknüpfen Sie ihn. Anschließend gehe ich folgendermaßen vor: Wenn libstdc ++ ihn vollständig implementiert hat, muss ich nur diesen Block entfernen und der Code bleibt gleich:
namespace std
{
using boost::regex;
using boost::regex_error;
using boost::regex_replace;
using boost::regex_search;
using boost::regex_match;
using boost::smatch;
namespace regex_constants = boost::regex_constants;
}
Ich werde nicht darüber streiten, ob das eine schlechte Idee ist oder nicht. Ich werde jedoch argumentieren, dass es es für mein Projekt sauber hält und es gleichzeitig spezifisch macht: Richtig, ich muss Boost verwenden, aber ich verwende es so, wie es libstdc ++ irgendwann haben wird. Ja, das Starten eines eigenen Projekts und das Beginnen mit einem Standard (...) am Anfang trägt wesentlich dazu bei, Wartung, Entwicklung und alles, was mit dem Projekt zu tun hat, zu unterstützen!
Nur um etwas zu verdeutlichen: Ich halte es eigentlich nicht für eine gute Idee, einen Namen einer Klasse / was auch immer in der STL absichtlich und genauer anstelle von zu verwenden. Die Zeichenfolge ist für mich die Ausnahme (ignorieren Sie das erste, obige oder zweite Wortspiel, wenn Sie müssen) für mich, da mir die Idee von 'Zeichenfolge' nicht gefallen hat.
So wie es ist, bin ich immer noch sehr voreingenommen gegenüber C und voreingenommen gegenüber C ++. Sparsame Details, vieles, woran ich arbeite, passt mehr zu C (aber es war eine gute Übung und eine gute Möglichkeit, mich dazu zu bringen, a. Eine andere Sprache zu lernen und b. Nicht weniger voreingenommen gegenüber Objekten / Klassen / usw. zu sein, was vielleicht besser gesagt wird als weniger verschlossen, weniger arrogant und mehr akzeptierend.). Aber was nützlich ist , ist das, was einige bereits vorgeschlagen haben: Ich verwende tatsächlich list (es ist ziemlich allgemein, nicht wahr?) Und sortiere (dasselbe), um zwei zu benennen, die einen Namenskonflikt verursachen würden, wenn ich das tun würde using namespace std;
, und so weiter Zu diesem Zweck ziehe ich es vor, spezifisch zu sein, die Kontrolle zu behalten und zu wissen, dass ich es spezifizieren muss, wenn ich beabsichtige, dass es die Standardverwendung ist. Einfach gesagt: keine Annahme erlaubt.
Und was Boosts Regex angeht std
. Ich mache das für die zukünftige Integration und - ich gebe wieder zu, dass dies Voreingenommenheit ist - ich denke nicht, dass es so hässlich ist wie boost::regex:: ...
. In der Tat ist das eine andere Sache für mich. Es gibt viele Dinge in C ++, die ich in Aussehen und Methoden noch nicht vollständig akzeptiert habe (ein weiteres Beispiel: Variadische Vorlagen versus Var-Argumente [obwohl ich zugebe, dass Variadische Vorlagen sehr, sehr nützlich sind!]). Sogar diejenigen, die ich akzeptiere, waren schwierig und ich habe immer noch Probleme mit ihnen.
Nach meinen Erfahrungen können Sie, wenn Sie mehrere Bibliotheken haben, die beispielsweise verwenden cout
, aber für einen anderen Zweck die falsche verwenden cout
.
Wenn ich zum Beispiel using namespace std;
und using namespace otherlib;
und nur cout
(was zufällig in beiden ist) anstelle von std::cout
(oder 'otherlib::cout'
) eingebe, verwenden Sie möglicherweise die falsche und erhalten Fehler. Es ist viel effektiver und effizienter zu bedienen std::cout
.
Es ist von Fall zu Fall. Wir möchten die "Gesamtbetriebskosten" der Software über ihre Lebensdauer minimieren. „Using namespace std“ besagt , hat einige Kosten, aber nicht mit ihm auch eine Kosten in Lesbarkeit hat.
Die Leute weisen zu Recht darauf hin, dass bei Verwendung der Standardbibliothek, wenn die Standardbibliothek neue Symbole und Definitionen einführt, der Code nicht mehr kompiliert wird und Sie möglicherweise gezwungen sind, Variablen umzubenennen. Und dennoch ist dies auf lange Sicht wahrscheinlich gut, da zukünftige Betreuer vorübergehend verwirrt oder abgelenkt sein werden, wenn Sie ein Schlüsselwort für einen überraschenden Zweck verwenden.
Sie möchten beispielsweise keine Vorlage namens Vektor haben, die nicht allen anderen bekannt ist. Und die Anzahl der neuen Definitionen, die auf diese Weise in die C ++ - Bibliothek eingeführt wurden, ist klein genug, dass sie möglicherweise einfach nicht auftauchen. Es ist ein Kosten mit dieser Art von Veränderung zu tun, aber die Kosten sind nicht hoch und wird durch die Klarheit gewonnen , indem nicht mit Offset - std
Symbolnamen für andere Zwecke.
Angesichts der Anzahl der Klassen, Variablen und Funktionen kann die Angabe std::
aller Klassen Ihren Code um 50% aufblähen und es schwieriger machen, sich zurechtzufinden. Ein Algorithmus oder Schritt in einer Methode, der auf einem Bildschirm voller Code ausgeführt werden kann, erfordert nun das Scrollen hin und her, um zu folgen. Dies ist ein echter Kostenfaktor. Es mag wohl keine hohen Kosten sein, aber Leute, die leugnen, dass es überhaupt existiert, sind unerfahren, dogmatisch oder einfach falsch.
Ich würde die folgenden Regeln anbieten:
std
unterscheidet sich von allen anderen Bibliotheken. Es ist die einzige Bibliothek, die jeder grundsätzlich kennen muss, und meiner Ansicht nach wird sie am besten als Teil der Sprache betrachtet. Im Allgemeinen gibt es einen hervorragenden Fall,using namespace std
auch wenn es keine für andere Bibliotheken gibt.Erzwingen Sie niemals die Entscheidung dem Autor einer Kompilierungseinheit (einer CPP-Datei), indem Sie diese
using
in einen Header einfügen . Immer verschieben die Entscheidung der Übersetzungseinheit Autor. Selbst in einem Projekt, das sich für die Verwendungusing namespace std
überall entschieden hat, können einige Module in Ordnung sein, die am besten als Ausnahmen von dieser Regel behandelt werden.Obwohl Sie mit der Namespace-Funktion viele Module mit gleich definierten Symbolen verwenden können, ist dies verwirrend. Halten Sie die Namen so weit wie möglich unterschiedlich. Selbst wenn Sie die Namespace-Funktion nicht verwenden , ist es auf lange Sicht wahrscheinlich besser, Ihre Klasse umzubenennen , wenn Sie eine Klasse mit dem Namen haben
foo
und eine Klasse mit dem Namenstd
einführenfoo
.Eine Alternative zur Verwendung von Namespaces besteht darin, Namespace-Symbole manuell durch Präfixe zu versehen. Ich habe zwei Bibliotheken, die ich seit Jahrzehnten benutze. Beide beginnen als C-Bibliotheken, wobei jedem Symbol "AK" oder "SCWin" vorangestellt ist. Im Allgemeinen ist dies so, als würde man das Konstrukt "using" vermeiden, aber man schreibt keine Doppelpunkte.
AK::foo()
ist stattdessenAKFoo()
. Dadurch wird der Code um 5-10% dichter und weniger ausführlich. Der einzige Nachteil besteht darin, dass Sie große Probleme haben, wenn Sie zwei solcher Bibliotheken mit demselben Präfix verwenden müssen. Beachten Sie, dass die X Window-Bibliotheken in dieser Hinsicht hervorragend sind, außer dass sie vergessen haben, dies mit ein paar #defines zu tun: TRUE und FALSE sollten XTRUE und XFALSE gewesen sein, und dies führte zu einem Namespace-Konflikt mit Sybase oder Oracle, die ebenfalls TRUE und FALSE verwendeten mit unterschiedlichen Werten! (ASCII 0 und 1 im Fall der Datenbank!) Ein besonderer Vorteil davon ist, dass es scheinbar ohne Präprozessordefinitionen gilt, während das C ++using
/namespace
System sie nicht verarbeitet. Ein schöner Vorteil davon ist, dass es eine organische Neigung von einem Teil eines Projekts zu einer Bibliothek gibt. In einer großen Anwendung von mir werden alle Fensterklassen vorangestelltWin
, alle Signalverarbeitungsmodule Mod und so weiter. Es besteht nur eine geringe Wahrscheinlichkeit, dass eines dieser Elemente wiederverwendet wird, sodass es keinen praktischen Vorteil hat, jede Gruppe zu einer Bibliothek zu machen, aber es wird in wenigen Sekunden deutlich, wie das Projekt in Teilprojekte zerfällt.
Bei nicht qualifizierten importierten Bezeichnern benötigen Sie externe Suchwerkzeuge wie grep , um herauszufinden, wo Bezeichner deklariert sind. Dies erschwert das Nachdenken über die Programmkorrektheit.
Es hängt davon ab, wo es sich befindet. Wenn es sich um einen allgemeinen Header handelt, verringern Sie den Wert des Namespace, indem Sie ihn mit dem globalen Namespace zusammenführen. Denken Sie daran, dies könnte eine gute Möglichkeit sein, Modulglobale zu erstellen.
Dies ist eine schlechte Praxis, die oft als globale Verschmutzung durch Namespaces bezeichnet wird. Probleme können auftreten, wenn mehr als ein Namespace denselben Funktionsnamen mit Signatur hat. Dann muss der Compiler nicht eindeutig entscheiden, welchen er aufrufen möchte std::cout
. Dies alles kann vermieden werden, wenn Sie den Namespace mit Ihrem Funktionsaufruf wie angeben . Hoffe das hilft. :) :)
Um Ihre Frage zu beantworten, sehe ich das praktisch so: Viele Programmierer (nicht alle) rufen den Namespace std auf. Daher sollte man es sich zur Gewohnheit machen, KEINE Dinge zu verwenden, die die gleichen Namen wie im Namespace std beeinflussen oder verwenden. Das ist sehr selbstverständlich, aber nicht so sehr im Vergleich zu der Anzahl möglicher zusammenhängender Wörter und Pseudonyme, die genau genommen gefunden werden können.
Ich meine wirklich ... zu sagen "Verlasse dich nicht darauf, dass dies vorhanden ist" bedeutet nur, dass du dich darauf verlässt, dass es NICHT vorhanden ist. Sie werden ständig Probleme haben, Codefragmente auszuleihen und ständig zu reparieren. Halten Sie einfach Ihre benutzerdefinierten und ausgeliehenen Inhalte in einem begrenzten Umfang, wie sie sein sollten, und gehen Sie SEHR sparsam mit Globals um (ehrlich gesagt sollten Globals fast immer das letzte Mittel sein, um "jetzt kompilieren, später vernünftig"). Wirklich, ich denke, es ist ein schlechter Rat von Ihrem Lehrer, weil die Verwendung von std sowohl für "cout" als auch für "std :: cout" funktioniert, aber NICHT für "std :: cout". Sie werden nicht immer das Glück haben, Ihren gesamten Code zu schreiben.
HINWEIS: Konzentrieren Sie sich nicht zu sehr auf Effizienzprobleme, bis Sie tatsächlich ein wenig über die Funktionsweise von Compilern gelernt haben. Mit ein wenig Erfahrung im Codieren müssen Sie nicht so viel über sie lernen, bevor Sie erkennen, wie viel sie in der Lage sind, guten Code in etwas Einfaches zu verallgemeinern. Genauso einfach, als ob Sie das Ganze in C geschrieben hätten. Guter Code ist nur so komplex, wie er sein muss.