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

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

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

15 ответов


пространства имен являются пакетами по существу. Их можно использовать так:

namespace MyNamespace
{
  class MyClass
  {
  };
}

потом в коде:

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

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

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

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


чтобы не сказать все, что Марк Ингрэм уже сказал, небольшой совет по использованию пространств имен:

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

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

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

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

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

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

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

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

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

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

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

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

самый безопасный способ использования "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;" не рекомендуется Скоттом Мейерс (я точно не помню, какую именно книгу, но при необходимости могу ее найти).

Состав Пространства Имен

пространства имен-это больше, чем пакеты. Другой пример можно найти в "языке программирования C++"Бьярне Страуструпа.

в "специальном выпуске", at 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() ;
}

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


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

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

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;

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

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

namespace ns {

class A
{
};

void print(A a)
{
}

}

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

ns::A a;
print(a);

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

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

namespace ns {

class A
{
};

}

void print(A a)
{
}

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

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

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

Если вы хотите лучше объяснить это поведение, вы можете обратиться к книге исключительный C++ от Херб Саттер!--21-->


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

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

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

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

namespace {
  const int CONSTANT = 42;
}

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

static const int CONSTANT = 42;

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


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

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

в файле square.h и

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

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


В Java:

package somepackage;
class SomeClass {}

В C++:

namespace somenamespace {
    class SomeClass {}
}

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

import somepackage;

И C++:

using namespace somenamespace;

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


вы также можете содержать "using namespace ..."внутри функции например:

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

@Мариус

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

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

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


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


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

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


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


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


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

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

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

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