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

StackOverflow https://stackoverflow.com/questions/41590

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Это было полезно?

Решение

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

Затем в коде:

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

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

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

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

Другие советы

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

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

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

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

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

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

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

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

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

В C ++ вы могли бы присвоить пространство имен всему коду в вашем модуле.Например, для модуля MyModule.dll вы могли бы присвоить его коду пространство имен 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++".

В "Специальном выпуске", по адресу 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 ++ от Херба Саттера

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

На самом деле 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

[Февраль.2014 - (Неужели прошло так много времени?):Этот конкретный пример теперь неоднозначен, как указывает Джоуи ниже.Повышение и ЗППП::теперь у каждого есть shared_ptr.]

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

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

Способ, которым вы можете использовать классы из других пространств имен, на удивление очень похож на способ в java.Вы можете либо использовать "использовать пространство имен", которое аналогично инструкции "импортировать ПАКЕТ", напримериспользуйте ЗППП.Или вы указываете пакет как префикс класса, разделенный символом "::", напримерstd::строка.Это похоже на "java.lang.String" в Java.

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top