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?


30
Jest też opcja 3: Podaj nam pełną nazwę, np int MyNamespace::MyClass::foo() ....
— Benjamin Bannier


@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

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


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.


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

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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.