Как правильно использовать пространства имен в С++?

Я исхожу из фона Java, где используются пакеты, а не пространства имен. Я привык ставить классы, которые работают вместе, чтобы сформировать полный объект в пакеты, а затем повторно использовать их позже из этого пакета. Но теперь я работаю на С++.

Как вы используете пространства имен в С++? Создаете ли вы единое пространство имен для всего приложения или создаете пространства имен для основных компонентов? Если да, как вы создаете объекты из классов в других пространствах имен?

Ответ 1

Пространства имен - это пакеты по существу. Их можно использовать следующим образом:

namespace MyNamespace
{
  class MyClass
  {
  };
}

Затем в коде:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

Надеюсь, что это поможет.

Или, если вы хотите всегда использовать определенное пространство имен, вы можете сделать это:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

Изменить: После того, что сказал bernhardrusch, я вообще не использую синтаксис "using namespace x" вообще, я обычно явно укажите пространство имен при создании объектов (т.е. первый пример, который я показал).

И как вы спросили ниже, вы можете использовать столько пространств имен, сколько хотите.

Ответ 2

Чтобы не говорить все, Марк Ингрэм уже сказал немного подсказки для использования пространств имен:

Избегайте директивы "using namespace" в файлах заголовков - это открывает пространство имен для всех частей программы, которые импортируют этот заголовочный файл. В файлах реализации (*.cpp) это обычно не представляет большой проблемы - хотя я предпочитаю использовать директиву "using namespace" на уровне функции.

Я думаю, что пространства имен в основном используются, чтобы избежать конфликтов имен - не обязательно для организации структуры кода. Я бы организовал программы на С++ в основном с файлами заголовков/файловой структурой.

Иногда пространства имен используются в больших проектах на С++, чтобы скрыть детали реализации.

Дополнительное примечание к директиве use: Некоторые люди предпочитают использовать "использование" только для отдельных элементов:

using std::cout;  
using std::endl;

Ответ 3

Винсент Роберт прав в своем комментарии Как правильно использовать пространства имен в С++?.

Использование пространства имен

Пространства имен используются, по крайней мере, для предотвращения столкновения имен. В Java это выполняется с помощью идиомы "org.domain" (поскольку предполагается, что никто не будет использовать ничего, кроме своего собственного доменного имени).

В С++ вы можете предоставить пространство имен ко всему коду вашего модуля. Например, для модуля MyModule.dll вы можете указать его код в пространстве имен MyModule. Я вижу в другом месте кого-то, использующего MyCompany:: MyProject:: MyModule. Я думаю, это слишком много, но в целом мне кажется правильным.

Использование "using"

Использование должно использоваться с большой осторожностью, поскольку оно эффективно импортирует один (или все) символы из пространства имен в текущее пространство имен.

Это зло для этого в заголовочном файле, потому что ваш заголовок будет загрязнять каждый источник, включая его (он напоминает мне макросы...), и даже в исходном файле плохой стиль вне области функции, потому что он будет импортировать в глобальном масштабе - символы из пространства имен.

Самый безопасный способ использования "использования" - импортировать символы выбора:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

Вы увидите много "использования пространства имен std;" в обучающих или примерных кодах. Причина в том, чтобы уменьшить количество символов, чтобы облегчить чтение, а не потому, что это хорошая идея.

"с использованием пространства имен std;" не поощряется Скоттом Мейерсом (я не помню точно, какую книгу, но я могу найти ее в случае необходимости).

Состав пространства имен

Пространства имен больше, чем пакеты. Другой пример можно найти в Bjarne Stroustrup "Язык программирования С++".

В "Специальном выпуске" в 8.2.8 Состав пространства имен он описывает, как вы можете объединить два пространства имен AAA и BBB в другой, называемый CCC. Таким образом, CCC становится псевдонимом для AAA и BBB:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

Вы даже можете импортировать символы выбора из разных пространств имен, чтобы создать собственный интерфейс имён пространства имен. Мне еще предстоит найти практическое применение этого, но теоретически это круто.

Ответ 4

Я не видел упоминания об этом в других ответах, так что вот мои 2 канадские центы:

В разделе "using namespace" полезным утверждением является псевдоним пространства имен, позволяющий вам "переименовать" пространство имен, обычно, чтобы дать ему более короткое имя. Например, вместо:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

вы можете написать:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;

Ответ 5

Не слушайте, чтобы все люди говорили вам, что пространства имен - это просто пространства имен.

Они важны, потому что они рассматривают компилятор для применения принципа интерфейса. В принципе, это можно объяснить примером:

namespace ns {

class A
{
};

void print(A a)
{
}

}

Если вы хотите напечатать объект A, код будет следующим:

ns::A a;
print(a);

Обратите внимание, что мы явно не указывали пространство имен при вызове функции. Это принцип интерфейса: С++ рассматривает функцию, принимающую тип в качестве аргумента как часть интерфейса для этого типа, поэтому нет необходимости указывать пространство имен, поскольку этот параметр уже подразумевал пространство имен.

Теперь почему этот принцип важен? Представьте, что автор класса A не предоставил функцию print() для этого класса. Вы должны будете предоставить его самостоятельно. Поскольку вы хороший программист, вы определите эту функцию в своем собственном пространстве имен или, возможно, в глобальном пространстве имен.

namespace ns {

class A
{
};

}

void print(A a)
{
}

И ваш код может начать вызов функции print (a) там, где вы хотите. Теперь представьте себе, что спустя годы автор решает предоставить функцию print(), лучше вашей, потому что он знает внутренности своего класса и может сделать лучшую версию, чем ваша.

Затем авторы С++ решили, что его версия функции print() должна использоваться вместо той, которая указана в другом пространстве имен, для соблюдения принципа интерфейса. И что это "обновление" функции print() должно быть максимально простым, что означает, что вам не придется менять каждый вызов функции print(). Вот почему "функции интерфейса" (функция в том же пространстве имен, что и класс) могут быть вызваны без указания пространства имен в С++.

И вот почему вы должны рассматривать пространство имен С++ как "интерфейс", когда используете его, и имейте в виду принцип интерфейса.

Если вы хотите лучше объяснить это поведение, вы можете обратиться к книге Исключительный С++ из Herb Sutter

Ответ 6

Большие проекты на С++, которые я видел, вряд ли использовали более одного пространства имен (например, boost library).

Фактически boost использует тонны пространств имен, обычно каждая часть boost имеет собственное пространство имен для внутренней работы, а затем может помещать только открытый интерфейс в расширение пространства имен верхнего уровня.

Лично я считаю, что чем больше база кода становится, тем более важными пространствами имен становятся даже внутри одного приложения (или библиотеки). На работе мы помещаем каждый модуль нашего приложения в собственное пространство имен.

Еще одно использование (без каламбура) пространств имен, которые я использую много, - это анонимное пространство имен:

namespace {
  const int CONSTANT = 42;
}

Это в основном то же самое, что:

static const int CONSTANT = 42;

Использование анонимного пространства имен (вместо статического), однако, рекомендуется, чтобы код и данные отображались только внутри текущего модуля компиляции в С++.

Ответ 7

Также обратите внимание, что вы можете добавить в пространство имен. Это более понятно с примером, я имею в виду, что вы можете иметь:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

в файле square.h и

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

в файле cube.h. Это определяет единое пространство имен MyNamespace (то есть вы можете определить одно пространство имен для нескольких файлов).

Ответ 8

В Java:

package somepackage;
class SomeClass {}

В С++:

namespace somenamespace {
    class SomeClass {}
}

И используя их, Java:

import somepackage;

И С++:

using namespace somenamespace;

Кроме того, полные имена - "somepackge.SomeClass" для Java и "somenamespace:: SomeClass" для С++. Используя эти соглашения, вы можете организовать, как вы привыкли в Java, включая создание совпадающих имен папок для пространств имен. Однако требования к папке- > package и file- > не существуют, поэтому вы можете самостоятельно определять свои папки и классы вне пакетов и пространств имен.

Ответ 9

@marius

Да, вы можете использовать несколько пространств имен за раз, например:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[февраль 2014 - (Неужели это так долго?): Этот конкретный пример теперь двусмыслен, как указывает Джо. Boost и std:: теперь у каждого есть shared_ptr.]

Ответ 10

Вы также можете использовать "использование пространства имен..." внутри функции, например:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

Ответ 11

Вообще говоря, я создаю пространство имен для тела кода, если полагаю, что, возможно, конфликты функций или типов с другими библиотеками могут возникнуть. Это также помогает маркировать код, ala boost::.

Ответ 12

Я предпочитаю использовать пространство имен верхнего уровня для пространства приложений и подпространств для компонентов.

То, как вы можете использовать классы из других пространств имен, на удивление очень похоже на способ в java. Вы можете использовать "использовать NAMESPACE", который похож на инструкцию "import PACKAGE", например. используйте std. Или вы указываете пакет как префикс класса, разделенного "::", например. std::string. Это похоже на "java.lang.String" в Java.

Ответ 13

Обратите внимание, что пространство имен в С++ действительно просто пространство имен. Они не предоставляют какой-либо инкапсуляции, которую делают пакеты на Java, поэтому вы, вероятно, не будете использовать их как можно больше.

Ответ 14

Я использовал пространства имен С++ так же, как и на С#, Perl и т.д. Это просто семантическое разделение символов между стандартными материалами библиотеки, материалами сторонних разработчиков и моим собственным кодом. Я бы разместил свое приложение в одном пространстве имен, а затем повторно используемый компонент библиотеки в другом пространстве имен для разделения.

Ответ 15

Другим различием между java и С++ является то, что в С++ иерархия пространства имен не нуждается в макете файловой системы. Поэтому я склонен размещать всю библиотеку многократного использования в одном пространстве имен и подсистемы внутри библиотеки в подкаталогах:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

Я бы поставил подсистемы в вложенные пространства имен, если была возможность конфликта имен.