"Using Namespace" in C ++ - Headern

119

In all unseren C ++ - Kursen setzen alle Lehrer immer using namespace std;direkt nach dem #includes in ihre .hDateien. Dies scheint mir seitdem gefährlich zu sein, wenn ich diesen Header in ein anderes Programm einbinde, wird der Namespace in mein Programm importiert, möglicherweise ohne es zu merken, zu beabsichtigen oder zu wollen (die Aufnahme von Headern kann sehr tief verschachtelt sein).

Meine Frage ist also doppelt: Habe ich Recht, dass using namespacesie nicht in Header-Dateien verwendet werden sollte, und / oder gibt es eine Möglichkeit, sie rückgängig zu machen, etwa:

//header.h
using namespace std {
.
.
.
}

Noch eine Frage in die gleiche Richtung: Sollte eine Header-Datei #includealle Header enthalten, die die entsprechende .cppDatei benötigt, nur diejenigen, die für die Header-Definitionen benötigt werden, und die .cppDatei #includeden Rest oder keine lassen und alles deklarieren, was sie benötigt extern?
Die Argumentation hinter der Frage ist dieselbe wie oben: Ich möchte keine Überraschungen beim Einfügen von .hDateien.

Wenn ich recht habe, ist das auch ein häufiger Fehler? Ich meine in der realen Programmierung und in "echten" Projekten da draußen.

Danke dir.

Baruch
quelle
3
Als Randnotiz: Wenn Sie aufgrund von using namespaceAnweisungen Namenskollisionen erhalten, können Sie den vollständig qualifizierten Namen verwenden, um das Problem zu lösen.
Marius Bancila

Antworten:

115

Sie sollten definitiv NICHT using namespacein Headern verwenden, genau aus dem Grund, den Sie sagen, dass dies die Bedeutung von Code in anderen Dateien, die diesen Header enthalten, unerwartet ändern kann. Es gibt keine Möglichkeit, einen rückgängig zu machen, using namespacewas ein weiterer Grund ist, warum er so gefährlich ist. Normalerweise verwende ich nur grepoder ähnliches, um sicherzustellen, dass dies using namespacenicht in Kopfzeilen aufgerufen wird, anstatt etwas Komplizierteres auszuprobieren. Wahrscheinlich kennzeichnen dies auch statische Codeprüfer.

Der Header sollte nur die Header enthalten, die zum Kompilieren benötigt werden. Eine einfache Möglichkeit, dies durchzusetzen, besteht darin, vor allen anderen Headern immer zuerst den eigenen Header jeder Quelldatei einzuschließen. Dann kann die Quelldatei nicht kompiliert werden, wenn der Header nicht in sich geschlossen ist. In einigen Fällen, z. B. in Bezug auf Implementierungsdetailklassen innerhalb einer Bibliothek, können Sie Forward-Deklarationen verwenden, anstatt #includedie volle Kontrolle über die Definition einer solchen Forward-Deklarationsklasse zu haben.

Ich bin mir nicht sicher, ob ich es allgemein nennen würde, aber es taucht definitiv ab und zu auf, normalerweise geschrieben von neuen Programmierern, die sich der negativen Konsequenzen nicht bewusst sind. In der Regel werden Probleme nur durch ein wenig Aufklärung über die Risiken behoben, da sie relativ einfach zu beheben sind.

Mark B.
quelle
2
Können wir usingAnweisungen in unseren .cppDateien verwenden? Die 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators sind der Tod bis in die Fingerspitzen.
Christopher
1
und wie sollen wir die templateFunktionen rationalisieren - die in den Headern sein sollen? typedefs?
Christopher
1
@donlan, anscheinend haben Sie eine ganze Weile keine Antwort erhalten ... Ja, Sie können usingAnweisungen in .cppDateien ohne große Bedenken verwenden, da der Bereich nur auf diese Datei beschränkt ist, dies jedoch niemals vor einer #includeAnweisung. Was die in Headern definierten Vorlagenfunktionen betrifft, kenne ich leider keine andere gute Lösung als nur das Schreiben des Namespace ... Vielleicht könnten Sie eine usingDeklaration in einen separaten Bereich einfügen { /* using statement in between brackets */ }, der zumindest verhindern würde, dass sie aus der aktuellen Datei entweicht .
tjwrona1992
26

Punkt 59 in Sutters und Alexandrescus "C ++ Coding Standards: 101 Regeln, Richtlinien und Best Practices" :

59. Schreiben Sie keine Namespace-Verwendungen in eine Header-Datei oder vor ein #include.

Namespaces usingdienen Ihrer Bequemlichkeit und nicht anderen, die Sie anderen zufügen möchten: Schreiben Sie niemals eine usingErklärung oder eine usingAnweisung vor einer #includeAnweisung.

Folgerung: Schreiben Sie in Header-Dateien keine usingDirektiven oder usingDeklarationen auf Namespace-Ebene . Stattdessen qualifizieren Sie alle Namen explizit für den Namespace.

Eine Header-Datei ist ein Gast in einer oder mehreren Quelldateien. Eine Header-Datei, die usingDirektiven und Deklarationen enthält, bringt auch ihre Rowdy Buddies rüber.

Eine using Erklärung bringt einen Kumpel herein. Eine using Direktive bringt alle Buddies in den Namespace. Die Verwendung durch Ihre Lehrer using namespace std;ist eine Verwendungsrichtlinie.

Im Ernst, wir haben Namespaces, um Namenskonflikte zu vermeiden. Eine Header-Datei soll eine Schnittstelle bereitstellen. Die meisten Header sind unabhängig davon, welcher Code sie jetzt oder in Zukunft enthalten kann. Durch Hinzufügen von usingAnweisungen zur internen Bequemlichkeit innerhalb des Headers werden diese praktischen Namen allen potenziellen Clients dieses Headers hinzugefügt. Das kann zu Namenskonflikten führen. Und es ist einfach unhöflich.

Andy Thomas
quelle
12

Sie müssen vorsichtig sein, wenn Sie Header in Header einfügen. In großen Projekten kann eine sehr verwickelte Abhängigkeitskette erstellt werden, die größere / längere Neuerstellungen auslöst, als tatsächlich erforderlich waren. In diesem Artikel und seinen Folgemaßnahmen erfahren Sie mehr über die Bedeutung einer guten physischen Struktur in C ++ - Projekten.

Sie sollten Header nur dann in einen Header einfügen, wenn dies unbedingt erforderlich ist (wenn die vollständige Definition einer Klasse erforderlich ist) und die Vorwärtsdeklaration verwenden, wo immer Sie können (wenn die Klasse erforderlich ist, handelt es sich um einen Zeiger oder eine Referenz).

Bei Namespaces verwende ich normalerweise den expliziten Namespace-Bereich in meinen Header-Dateien und using namespacefüge nur einen in meine CPP-Dateien ein.

Mike O'Connor
quelle
1
Wie rationalisieren Sie die templateFunktionsdeklaration? das muss im header vorkommen, nein?
Christopher
6

Lesen Sie die Codierungsstandards für das Goddard Space Flight Center (für C und C ++). Das stellt sich als etwas schwieriger heraus als früher - siehe die aktualisierten Antworten auf die SO-Fragen:

Der GSFC C ++ - Codierungsstandard lautet:

§3.3.7 Jede Header-Datei enthält #includedie Dateien, die zum Kompilieren benötigt werden, anstatt Benutzer zu #includeden erforderlichen Dateien zu zwingen . #includesbeschränkt sich auf das, was der Header benötigt; andere #includessollten in der Quelldatei platziert werden.

Die erste der Fragen, auf die verwiesen wird, enthält jetzt ein Zitat aus dem GSFC C-Codierungsstandard und die Begründung, aber die Substanz ist am Ende dieselbe.

Jonathan Leffler
quelle
5

Sie haben Recht, dass using namespaceim Header gefährlich ist. Ich weiß nicht, wie ich es rückgängig machen soll. Es ist leicht zu erkennen, aber suchen Sie einfach using namespacein den Header-Dateien. Aus diesem letzten Grund ist es in realen Projekten ungewöhnlich. Erfahrene Mitarbeiter werden sich bald beschweren, wenn jemand so etwas tut.

In realen Projekten wird versucht, die Anzahl der enthaltenen Dateien zu minimieren. Je weniger Sie einschließen, desto schneller wird die Kompilierung durchgeführt. Das spart allen Zeit. Wenn die Header-Datei jedoch davon ausgeht, dass etwas davor enthalten sein sollte, sollte sie es selbst enthalten. Andernfalls sind die Header nicht in sich geschlossen.

Öö Tiib
quelle
4

Du hast recht. Und jede Datei sollte nur die Header enthalten, die von dieser Datei benötigt werden. Was "Ist es in Projekten der realen Welt üblich, Dinge falsch zu machen?" - Oh ja!


quelle
4

Wie alle Dinge in der Programmierung sollte Pragmatismus den Dogmatismus, IMO, für sich gewinnen.

Solange Sie die Entscheidung projektweit treffen ("Unser Projekt verwendet STL ausgiebig und wir möchten nicht alles mit std :: voranstellen müssen"), sehe ich das Problem damit nicht. Das einzige, was Sie riskieren, sind schließlich Namenskollisionen, und angesichts der Allgegenwart von STL ist es unwahrscheinlich, dass dies ein Problem darstellt.

Wenn es sich jedoch um eine Entscheidung eines Entwicklers in einer einzelnen (nicht privaten) Header-Datei handelt, kann ich sehen, wie dies zu Verwirrung im Team führen würde und vermieden werden sollte.

ijprest
quelle
4

In Bezug auf "Gibt es eine Möglichkeit, [eine usingErklärung] rückgängig zu machen ?"

Ich halte es für nützlich, darauf hinzuweisen, dass usingErklärungen vom Geltungsbereich betroffen sind.

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

Also effektiv ja. Durch die Einschränkung des Geltungsbereichs der usingErklärung bleibt ihre Wirkung nur in diesem Geltungsbereich bestehen. Es wird rückgängig gemacht, wenn dieser Bereich endet.

Wenn die usingDeklaration in einer Datei außerhalb eines anderen Bereichs deklariert wird, hat sie einen Dateibereich und wirkt sich auf alles in dieser Datei aus.

Wenn sich die usingDeklaration im Fall einer Header-Datei im Dateibereich befindet, erstreckt sich dies auf den Bereich jeder Datei, in der der Header enthalten ist.

YoungJohn
quelle
2
Sie scheinen der einzige zu sein, der die eigentliche Frage verstanden hat ... meine Kompilierung ist jedoch nicht sehr glücklich darüber, dass ich sie innerhalb der Klassenverzögerung verwende.
Rustypaper
Diese Antwort könnte noch besser gemacht werden, indem das Problem mit der Idee des OP erklärt wird, wie der Umfang funktionieren soll (wie das namespaceDeklarationsmaterial) und wie er tatsächlich funktioniert (wie eine Variable). {}Wenn Sie es einschließen, schränken Sie seinen Umfang ein, {}nachdem Sie nichts damit zu tun haben. Das ist eine zufällige Art und Weise, wie das using namespaceglobal angewendet wird.
TafT
2

Ich glaube, Sie können 'using' in C ++ - Headern sicher verwenden, wenn Sie Ihre Deklarationen in einen verschachtelten Namespace wie folgt schreiben:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Dies sollte nur die in 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' deklarierten Dinge ohne die verwendeten Namespaces enthalten. Ich habe es auf mingw64 Compiler getestet.

AnArrayOfFunctions
quelle
Dies ist eine nützliche Technik, die ich vorher noch nicht gesehen hatte. Vielen Dank. Normalerweise war ich damit einverstanden, die Vollbereichsqualifizierung zu verwenden und usingDeklarationen in Funktionsdefinitionen einzufügen, wo ich kann, damit sie Namespaces außerhalb der Funktion nicht verschmutzen. Aber jetzt möchte ich benutzerdefinierte C ++ 11-Literale in einer Header-Datei verwenden, und gemäß der üblichen Konvention sind die Literaloperatoren durch einen Namespace geschützt. Ich möchte sie jedoch nicht in Konstruktorinitialisiererlisten verwenden, die nicht in einem Bereich liegen, in dem ich eine umweltfreundliche usingDeklaration verwenden kann. Das ist also großartig, um dieses Problem zu lösen.
Anthony Hall
Obwohl ein unglücklicher Nebeneffekt dieses Musters darin besteht, dass alle Klassen, die im innersten Namespace deklariert sind, in Compiler-Fehlermeldungen mit dem vollständig qualifizierten Namen angezeigt werden : error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Zumindest passiert das für mich in g ++.
Anthony Hall