C ++: Przestrzenie nazw - Jak poprawnie używać w nagłówkach i plikach źródłowych?

88

Rozważmy parę dwóch plików źródłowych: plik deklaracji interfejsu ( *.hlub *.hpp) i plik jego implementacji ( *.cpp).

Niech *.hplik będzie podobny do następującego:

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

Widziałem dwie różne praktyki dotyczące używania przestrzeni nazw w plikach źródłowych:

*.cpp pokazując praktykę nr 1:

#include "MyClass.h"
using namespace MyNamespace;

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

*.cpp pokazując praktykę nr 2:

#include "MyClass.h"
namespace MyNamespace {

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

}

Moje pytanie: czy są jakieś różnice między tymi dwiema praktykami i czy jedna jest uważana za lepszą od drugiej?

nickolay
źródło
30
Jest też opcja 3: Podaj nam pełną nazwę, np int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Możliwy duplikat: stackoverflow.com/questions/7789163/ ...
David
@Dave nie jest duplikatem. Te pytania wzajemnie się uzupełniają. Polecam dodanie linku podanego przez Dave'a jako „Przeczytaj także ...” do tego pytania. Moje pytanie pomoże nowicjuszom wybrać odpowiedni styl.
nickolay
Możliwy duplikat: stackoverflow.com/questions/8210935/…
Firedragon

Odpowiedzi:

62

Z punktu widzenia czytelności kodu, moim zdaniem prawdopodobnie lepiej jest użyć metody nr 2 z tego powodu:

Jednocześnie może istnieć usingwiele przestrzeni nazw, a każdy obiekt lub funkcja zapisana poniżej tej linii może należeć do dowolnej z tych przestrzeni nazw (z wyjątkiem konfliktów nazw). Zawijanie całego pliku w namespacebloku jest bardziej wyraźne i pozwala zadeklarować nowe funkcje i zmienne, które należą do tej przestrzeni nazw, również w pliku .cpp

Dan F
źródło
Pytanie, które Dave połączył w swoim komentarzu do twojego pytania, nakreśla również kilka kluczowych punktów w różnicach (jeśli w ogóle) między dwiema metodami, na które patrzysz
Dan F
Chłopaki, naprawdę nie wiem, czyją odpowiedź wybrać. Przecinają się, a jednocześnie uzupełniają.
nickolay
Wystarczy skomentować, aby potwierdzić, że niektóre IDE, takie jak CLion, wykryją implementacje tylko wtedy, gdy użyjesz opcji / praktyki # 2.
PedroTanaka
@PedroTanaka czy nadal tak jest? Nie zauważyłem takiego problemu.
John McFarlane
@JMcF Nie sprawdzałem od czasu opublikowania komentarza. We wczesnych wersjach Cliona wystąpił problem.
PedroTanaka
51

Najwyraźniejsza jest opcja, której nie pokazałeś:

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

Jest również bardzo rozwlekły; za dużo dla większości ludzi. Ponieważ using namespacejest to recepta na konflikty nazw, przynajmniej z mojego doświadczenia, i powinno się ich unikać, z wyjątkiem bardzo ograniczonych zakresów i miejsc, generalnie używam twojego # 2.

James Kanze
źródło
3
Dzięki bardzo jasne. Razem stworzyliśmy dobrą stronę FAQ dla użytkowników przestrzeni nazw. :)
nickolay
2
Chłopaki, naprawdę nie wiem, czyją odpowiedź wybrać. Przecinają się, a jednocześnie uzupełniają.
nickolay
10

Czy są jakieś różnice między tymi dwoma praktykami

Tak. # 1 i # 2 to przykłady odpowiednio dyrektywy using i definicji przestrzeni nazw . W tym przypadku są one faktycznie takie same, ale mają inne konsekwencje. Na przykład, jeśli obok wprowadzisz nowy identyfikator MyClass::foo, będzie on miał inny zakres:

# 1:

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

# 2:

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

czy jeden jest uważany za lepszy od drugiego?

# 1 Zalety: trochę bardziej zwięzły; trudniej przypadkowo wprowadzić coś w MyNamespacenieświadomy sposób. Wady: może przypadkowo pobrać istniejące identyfikatory.

# 2 Zalety: jaśniejsze, że definicje istniejących identyfikatorów i deklaracje nowych identyfikatorów należą do obu MyNamespace. Wady: łatwiej jest nieumyślnie wprowadzić identyfikatory do MyNamespace.

Krytyką obu punktów 1 i 2 jest to, że odnoszą się one do całej przestrzeni nazw, podczas gdy prawdopodobnie zależy Ci tylko na definicji członków MyNamespace::MyClass. Jest to uciążliwe i słabo przekazuje zamiar.

Możliwą alternatywą dla # 1 jest deklaracja użycia, która zawiera tylko interesujący Cię identyfikator:

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

int MyClass::foo() { ... }
John McFarlane
źródło
4

Chciałbym również dodać, że jeśli z jakiegoś powodu zdecydujesz się zaimplementować specjalizację szablonu w pliku cpp i polegasz tylko na using namespacetym, napotkasz następujący problem:

// .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(){}

W przeciwnym razie, jeśli zastosujesz metodę nr 2, będzie dobrze.

Jordania
źródło
0

Chciałbym dodać jeszcze jeden sposób, używając deklaracji using :

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

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

W ten sposób oszczędza się wielokrotnego wpisywania nazwy przestrzeni nazw, jeśli klasa ma wiele funkcji

Joanna
źródło