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

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

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


person Marius    schedule 03.09.2008    source источник


Ответы (16)


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

namespace MyNamespace
{
  class MyClass
  {
  };
}

Затем в коде:

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

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

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

person Mark Ingram    schedule 03.09.2008
comment
ИМО, лучше просто привыкнуть к префиксу пространства имен std к символам, а не использовать using вообще. Так что я всегда пишу std::cout или std::string, потому что теперь я их так называю. Я бы никогда не стал просто писать cout. - person Tom Savage; 05.12.2009
comment
Хотя это очень верно для std, я лично считаю это гораздо менее важным, когда вы имеете дело с небольшими библиотеками. Часто вы можете просто использовать using namespace FooBario;, особенно если вы используете значительное количество типов из библиотеки. - person jkerian; 30.10.2010
comment
@jkerian, я понимаю вашу точку зрения, но я не согласен, потому что конфликты имен (на мой взгляд) с большей вероятностью исходят именно из таких небольших библиотек. Большинство людей стараются не называть классы / функции такими же, как в STL. Тем не менее, я согласен с тем, что using namespace X; следует по возможности избегать в файлах заголовков. - person Alan Turing; 28.05.2011
comment
@LexFridman Большинство людей стараются не называть классы / функции такими же, как в STL - это ТАК НЕ ИСТИНА. Например, если бы мне пришлось написать какой-то очень специализированный код ввода-вывода для какого-то странного оборудования, я бы никогда, никогда не использовал бы ничего, кроме mylibrary::endl для представления моей собственной особой последовательности новой строки. Я имею в виду, зачем придумывать имена? - person ; 24.10.2011
comment
Мой компилятор по-прежнему не распознает пространство имен, хотя я хочу указать его явно и включаю файл, в котором оно объявлено. - person bgenchel; 13.11.2015
comment
Я видел, как кто-то использовал: namespace someNamespace {class MyClass; } и затем определите его: class someNamespace :: MyClass: public otherClass {// обычный материал класса} Какова цель размещения MyClass с пространством имен в верхней части заголовка? - person user1919249; 19.10.2016

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

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

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

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

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

using std::cout;  
using std::endl;
person bernhardrusch    schedule 03.09.2008
comment
Одно из преимуществ использования пространства имен на уровне функции, как вы предлагаете, а не на уровне файла .cpp или уровне блока пространства имен {} внутри .cpp, заключается в том, что это очень помогает при сборках с одной единицей компиляции. Использование пространства имен является транзитивным и применяется к пространству имен A через блоки дискретного пространства имен A {} в одном и том же модуле, поэтому для сборок с одним модулем компиляции вы быстро в конечном итоге используете все, если они выполняются на уровне файла или блока пространства имен. - person idij; 18.12.2014
comment
using std::cout; - это объявление об использовании - person Konstantin; 19.04.2015
comment
Можно ли использовать несколько имен из одного пространства имен в одном операторе? Что-то вроде using std::cout, std::endl; или даже using std::cout, endl;. - person AlQuemist; 29.04.2016
comment
Можно использовать using namespace x в заголовке, если он находится в другом пространстве имен. Я бы не рекомендовал это вообще, но он не загрязняет глобальное пространство имен. - person Praxeolitic; 10.06.2016

Винсент Роберт прав в своем комментарии Как правильно использовать пространства имен в 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() ;
}

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

person paercebal    schedule 17.09.2008
comment
Не могли бы вы уточнить, укажите пространство имен для всего кода в вашем модуле? Что является хорошей практикой для инкапсуляции в module. Например, у меня есть класс комплексных чисел и внешних функций, связанных с комплексными числами. Этот класс и эти две функции должны находиться в одном пространстве имен? - person yanpas; 10.01.2016

Я не видел упоминания об этом в других ответах, поэтому вот мои 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;
person Éric Malenfant    schedule 21.10.2009

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

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

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 ++ от Херба Саттера

person Vincent Robert    schedule 07.09.2008
comment
На самом деле вам нужно изменить каждый вызов print (), если добавлен ns :: Print, но компилятор будет отмечать каждый вызов как неоднозначный. Тихо переключиться на новую функцию было бы ужасной идеей. - person Eclipse; 25.10.2008
comment
Теперь мне интересно, имея то, что @Vincent сказал, что вам придется изменить все вызовы на печать, если автор предоставит функцию ns :: Print (), что вы пытались сказать? Что, когда автор добавил функцию ns :: Print (), вы можете просто удалить свою собственную реализацию? Или что вы просто добавите с помощью ns :: print () using-declare? Или что-то еще? Спасибо - person Vaska el gato; 22.06.2016

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

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

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

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

namespace {
  const int CONSTANT = 42;
}

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

static const int CONSTANT = 42;

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

person Community    schedule 07.09.2008
comment
Оба ваших примера эквивалентны const int CONSTANT = 42;, потому что константа верхнего уровня в области пространства имен уже подразумевает внутреннюю связь. Таким образом, в этом случае вам не нужно анонимное пространство имен. - person sellibitze; 21.10.2009

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

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

в файле square.h и

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

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

person OysterD    schedule 03.09.2008

В Java:

package somepackage;
class SomeClass {}

In C++:

namespace somenamespace {
    class SomeClass {}
}

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

import somepackage;

И C ++:

using namespace somenamespace;

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

person Staale    schedule 03.09.2008

@ мариус

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

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 :: now имеют shared_ptr.]

person Adam Hollidge    schedule 03.09.2008
comment
Обратите внимание, что std также имеет shared_ptr, поэтому использование пространств имен boost и std будет конфликтовать, когда вы попытаетесь использовать shared_ptr. - person Joey; 06.02.2014
comment
Это хороший пример того, почему многие производители программного обеспечения не одобряют импорт целых пространств имен таким образом. Не помешает всегда указывать пространство имен, и если они слишком длинные, создайте псевдоним или только важные конкретные классы из пространства имен. - person paddy; 16.03.2016

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

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

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

person Adam Hollidge    schedule 03.09.2008

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

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

person dmeister    schedule 03.09.2008

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

person Kristopher Johnson    schedule 03.09.2008

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

person spoulson    schedule 03.09.2008

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

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

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

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

person KeithB    schedule 03.09.2008

std :: cout

Префикс std :: указывает, что имена cout и endl определены внутри пространства имен с именем std. Пространства имен позволяют нам избежать непреднамеренных конфликтов между именами, которые мы определяем, и использованием тех же имен внутри библиотеки. Все имена, определенные стандартной библиотекой, находятся в пространстве имен std. При написании std :: cout используется оператор области видимости (оператор ::), чтобы сказать, что мы хотим использовать имя cout, которое определено в пространстве имен std. покажет более простой способ доступа к именам из библиотеки.

person nidhi kumari    schedule 22.03.2021