C ++: Namespaces - Wie verwende ich Header- und Quelldateien richtig?

85

Betrachten Sie ein Paar von zwei Quelldateien: eine Schnittstellendeklarationsdatei ( *.hoder *.hpp) und ihre Implementierungsdatei ( *.cpp).

Lassen Sie die *.hDatei wie folgt aussehen:

namespace MyNamespace {
  class MyClass {
  public:
    int foo();
  };
}

Ich habe zwei verschiedene Methoden zur Verwendung von Namespaces in Quelldateien gesehen:

*.cpp Übung 1 zeigen:

#include "MyClass.h"
using namespace MyNamespace;

int MyClass::foo() { ... }

*.cpp Übung 2 zeigen:

#include "MyClass.h"
namespace MyNamespace {

  int MyClass::foo() { ... }

}

Meine Frage: Gibt es Unterschiede zwischen diesen beiden Praktiken und wird eine als besser angesehen als die andere?

Nickolay
quelle
29
Es gibt auch Option 3: Nur uns den vollständigen Namen, z int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Mögliches Duplikat: stackoverflow.com/questions/7789163/…
David
@ Dave nicht duplizieren. Diese Fragen ergänzen sich. Empfehlen Sie, den von Dave bereitgestellten Link als "Lesen Sie auch ..." zu dieser Frage hinzuzufügen. Meine Frage hilft Anfängern bei der Auswahl des richtigen Stils.
Nickolay
Mögliche doppelte: stackoverflow.com/questions/8210935/...
Firedragon

Antworten:

61

Unter dem Gesichtspunkt der Lesbarkeit von Code ist es meiner Meinung nach wahrscheinlich besser, die Methode Nr. 2 aus diesem Grund zu verwenden:

Sie können usingmehrere Namespaces gleichzeitig sein, und jedes Objekt oder jede Funktion, die unter dieser Zeile geschrieben wird, kann zu einem dieser Namespaces gehören (mit Ausnahme von Namenskonflikten). Das Umschließen der gesamten Datei in einen namespaceBlock ist expliziter und ermöglicht es Ihnen, neue Funktionen und Variablen, die zu diesem Namespace gehören, auch in der CPP-Datei zu deklarieren

Dan F.
quelle
Die Frage, die Dave in seinem Kommentar mit Ihrer Frage verknüpft hat, umreißt auch einige wichtige Punkte in den Unterschieden (falls vorhanden) zwischen den beiden Methoden, die Sie betrachten
Dan F
Leute, ich weiß wirklich nicht, wessen Antwort ich auswählen soll. Sie haben Schnittpunkte und ergänzen sich.
Nickolay
Nur ein Kommentar, um zu bestätigen, dass einige IDEs wie CLion Implementierungen nur erkennen, wenn Sie Option / Übung 2 verwenden.
PedroTanaka
@ PedroTanaka ist das noch der Fall? Ich habe kein solches Problem bemerkt.
John McFarlane
@JMcF Ich habe seit der Veröffentlichung des Kommentars nicht mehr nachgesehen. In früheren Versionen von Clion trat das Problem auf.
PedroTanaka
50

Am klarsten ist die Option, die Sie nicht angezeigt haben:

int MyNamespace::MyClass::foo()
{
    //  ...
}

Es ist auch sehr ausführlich; zu viel für die meisten Menschen. Da using namespacees sich nach meiner Erfahrung um ein Rezept für Namenskonflikte handelt, das vermieden werden sollte, außer in sehr begrenzten Bereichen und Orten, verwende ich im Allgemeinen Ihre Nummer 2.

James Kanze
quelle
3
Vielen Dank ganz klar. Zusammen haben wir eine gute FAQ-Seite für Namespace-Benutzer erstellt. :)
Nickolay
2
Leute, ich weiß wirklich nicht, wessen Antwort ich auswählen soll. Sie haben Schnittpunkte und ergänzen sich.
Nickolay
10

Gibt es Unterschiede zwischen diesen beiden Praktiken?

Ja. # 1 und # 2 sind Beispiele für eine using-Direktive bzw. eine Namespace-Definition . Sie sind in diesem Fall praktisch gleich, haben aber andere Konsequenzen. Wenn Sie beispielsweise nebenan einen neuen Bezeichner einführen MyClass::foo, hat dieser einen anderen Gültigkeitsbereich:

# 1:

using namespace MyNamespace;
int x;  // defines ::x

# 2:

namespace MyNamespace {
  int x;  // defines MyNamespace::x
}

wird einer als besser angesehen als der andere?

# 1 Vorteile: etwas knapper; Es ist schwieriger, versehentlich etwas MyNamespaceunabsichtlich einzuführen . Nachteile: Möglicherweise werden vorhandene Kennungen unbeabsichtigt abgerufen.

# 2 Vorteile: Klarer, dass sowohl Definitionen vorhandener Bezeichner als auch Deklarationen neuer Bezeichner gehören MyNamespace. Nachteile: einfacher, unbeabsichtigt Bezeichner einzuführen MyNamespace.

Eine Kritik an # 1 und # 2 ist, dass sie sich auf einen ganzen Namespace beziehen, wenn Sie sich wahrscheinlich nur um die Definition von Mitgliedern von kümmern MyNamespace::MyClass. Dies ist hartnäckig und kommuniziert die Absicht schlecht.

Eine mögliche Alternative zu # 1 ist eine using-Deklaration, die nur die Kennung enthält, an der Sie interessiert sind:

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }
John McFarlane
quelle
4

Ich möchte auch hinzufügen, dass, wenn Sie sich aus irgendeinem Grund für die Implementierung einer Vorlagenspezialisierung in einer CPP-Datei entscheiden und sich nur darauf verlassen, dass using namespaceSie auf das folgende Problem stoßen:

// .h file
namespace someNameSpace
{
  template<typename T>
    class Demo
    {
      void foo();
    };
}

// .cpp file
using namespace someNameSpace;

template<typename T>
void Demo<T>::foo(){}

// this will produce
// error: specialization of 'template<class T> void someNameSpace::Demo<T>::foo()' in different namespace [-fpermissive]
template<>
void Demo<int>::foo(){}

Andernfalls ist dies in Ordnung, wenn Sie die Methode Nr. 2 anwenden.

Jordanien
quelle
0

Ich möchte mit der using-Deklaration noch einen weiteren Weg hinzufügen :

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

Auf diese Weise ersparen Sie sich die Eingabe des Namespace-Namens, wenn die Klasse viele Funktionen hat

Joanna
quelle