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



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



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

578   15  

15 ответов:

пространства имен являются пакетами по существу. Они могут быть использованы следующим образом:

namespace MyNamespace
{
  class MyClass
  {
  };
}

потом в коде:

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

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

Edit: после чего bernhardrusch сказал, что я вообще не использую синтаксис "using namespace 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++"Бьярне Страуструпа.

в "специальном выпуске", на 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 функция, принимающая тип в качестве аргумента как часть интерфейса для этого типа, поэтому нет необходимости указывать пространство имен, поскольку параметр уже подразумевал пространство имен.

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

namespace ns {

class A
{
};

}

void print(A a)
{
}

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

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

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

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

большие проекты 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, в том числе сделать соответствующие имена папок для пространств имен. Однако требования к папке - >пакету и файлу - >классу отсутствуют, поэтому вы можете назвать свои папки и классы независимо от пакетов и пространств имен.

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

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 -- (это действительно было так долго?): Этот конкретный пример теперь неоднозначен, как указывает Джоуи ниже. Boost и std:: теперь у каждого есть shared_ptr.]

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

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

способ использования классов из других пространств имен удивительно похож на способ в java. Вы можете использовать "use NAMESPACE", который похож на оператор" import PACKAGE", например, использовать 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();

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

Comments

    Ничего не найдено.