C ++: Пространства имен - как правильно использовать в заголовочных и исходных файлах?

88

Рассмотрим пару из двух исходных файлов: файл объявления интерфейса ( *.hили *.hpp) и файл его реализации ( *.cpp).

Пусть *.hфайл будет таким:

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

Я видел две разные практики использования пространств имен в исходных файлах:

*.cpp показываю практику №1:

#include "MyClass.h"
using namespace MyNamespace;

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

*.cpp показываю практику №2:

#include "MyClass.h"
namespace MyNamespace {

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

}

Мой вопрос: есть ли различия между этими двумя практиками и считается ли одна лучше другой?

Николай
источник
30
Также есть вариант 3: укажите полное имя, например int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Возможный дубликат: stackoverflow.com/questions/7789163/…
Дэвид
@ Дэйв не дублирует. Эти вопросы дополняют друг друга. Рекомендую добавить ссылку, предоставленную Дейвом, как «Читайте также ...» к этому вопросу. Мой вопрос поможет новичкам выбрать правильный стиль.
николай
Возможный дубликат: stackoverflow.com/questions/8210935/…
Firedragon

Ответы:

62

С точки зрения читабельности кода, на мой взгляд, по этой причине, вероятно, лучше использовать метод №2:

У вас может быть usingнесколько пространств имен одновременно, и любой объект или функция, написанные под этой строкой, могут принадлежать любому из этих пространств имен (за исключением конфликтов имен). Обертывание всего файла в namespaceблоке более явное и позволяет объявлять новые функции и переменные, которые принадлежат этому пространству имен, в файле .cpp.

Дэн Ф
источник
Вопрос, который Дэйв связал в своем комментарии к вашему вопросу, также описывает некоторые ключевые моменты различий (если таковые имеются) между двумя методами, на которые вы смотрите
Дэн Ф.
Ребята, уж точно не знаю чей выбрать ответ. Они пересекаются и дополняют друг друга.
николай
Просто комментирую, чтобы подтвердить, что некоторые IDE, такие как CLion, будут обнаруживать реализации только в том случае, если вы используете вариант / практику # 2.
PedroTanaka
@PedroTanaka, это все еще так? Я не заметил такой проблемы.
John McFarlane
@JMcF Я не проверял с тех пор, как опубликовал комментарий. В ранних версиях Clion проблема возникала.
PedroTanaka
51

Самым ясным является вариант, который вы не показали:

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

Это также очень многословно; слишком много для большинства людей. Поскольку using namespaceэто рецепт для конфликтов имен, по крайней мере, по моему опыту, и его следует избегать, за исключением очень ограниченных областей и мест, я обычно использую ваш № 2.

Джеймс Канце
источник
3
Спасибо очень ясно. Вместе мы сделали хорошую страницу часто задаваемых вопросов для пользователей пространств имен. :)
николай
2
Ребята, уж точно не знаю чей выбрать ответ. Они пересекаются и дополняют друг друга.
николай
10

Есть ли различия между этими двумя практиками?

Да. # 1 и # 2 являются примерами директивы using и определения пространства имен соответственно. В этом случае они фактически одинаковы, но имеют другие последствия. Например, если вы введете рядом новый идентификатор MyClass::foo, он будет иметь другую область действия:

№1:

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

# 2:

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

один считается лучше другого?

# 1 Плюсы: немного лаконичнее; труднее случайно что-то ввести вMyNamespace невольно. Минусы: может непреднамеренно втянуть существующие идентификаторы.

# 2 Плюсы: более ясно, что и определения существующих идентификаторов, и объявления новых идентификаторов принадлежат MyNamespace. Минусы: проще непреднамеренно ввести идентификаторы MyNamespace.

Критика как №1, так и №2 состоит в том, что они относятся ко всему пространству имен, тогда как вы, вероятно, заботитесь только об определении членов MyNamespace::MyClass. Это жестко и плохо передает намерения.

Возможная альтернатива # 1 - это объявление-использование, которое включает только интересующий вас идентификатор:

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

int MyClass::foo() { ... }
Джон Макфарлейн
источник
4

Я также хотел бы добавить, что если вы по какой-то причине решите реализовать специализацию шаблона в файле cpp и просто положитесь на него, using namespaceвы столкнетесь со следующей проблемой:

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

В противном случае, если вы примените метод №2, все будет в порядке.

Иордания
источник
0

Я хотел бы добавить еще один способ с использованием объявления-использования :

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

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

Это избавит вас от необходимости многократно вводить имя пространства имен, если у класса много функций.

Джоанна
источник