Как правильно использовать пространства имен в C ++? - PullRequest
227 голосов
/ 03 сентября 2008

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

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

Ответы [ 15 ]

161 голосов
/ 03 сентября 2008

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

Тогда в коде:

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

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

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

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

112 голосов
/ 03 сентября 2008

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

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

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

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

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

using std::cout;  
using std::endl;
76 голосов
/ 17 сентября 2008

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

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

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

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

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

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

В «Специальном издании» по адресу 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() ;
}

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

71 голосов
/ 21 октября 2009

Я не видел упоминаний об этом в других ответах, поэтому вот мои 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;
53 голосов
/ 07 сентября 2008

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

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

namespace ns {

class A
{
};

void print(A a)
{
}

}

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

ns::A a;
print(a);

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

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

namespace ns {

class A
{
};

}

void print(A a)
{
}

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

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

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

Если вам нужно более подробное объяснение этого поведения, вы можете обратиться к книге Exceptional C ++ от Herb Sutter

36 голосов
/ 07 сентября 2008

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

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

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

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

namespace {
  const int CONSTANT = 42;
}

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

static const int CONSTANT = 42;

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

18 голосов
/ 03 сентября 2008

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

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

в файле square.h и

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

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

11 голосов
/ 03 сентября 2008

В Java:

package somepackage;
class SomeClass {}

В C ++:

namespace somenamespace {
    class SomeClass {}
}

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

import somepackage;

И С ++:

using namespace somenamespace;

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

5 голосов
/ 03 сентября 2008

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

void test(const std::string& s) {
    using namespace std;
    cout << s;
}
5 голосов
/ 03 сентября 2008

@ 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.]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...