Пространства имен (C++)
Пространство имен — это декларативная область, в рамках которой определяются различные идентификаторы (имена типов, функций, переменных, и т. д.). Пространства имен используются для организации кода в виде логических групп и с целью избежания конфликтов имен, которые могут возникнуть, особенно в таких случаях, когда база кода включает несколько библиотек. Все идентификаторы в пределах пространства имен доступны друг другу без уточнения. Идентификаторы за пределами пространства имен могут получить доступ к членам с помощью полного имени для каждого идентификатора, например, с помощью объявления для одного идентификатора ( using std::string ) или директивы using для всех идентификаторов в пространстве имен ( using namespace std; ). std::vector vec; Код в файлах заголовков всегда должен содержать полное имя в пространстве имен.
В следующем примере показано объявление пространства имен и продемонстрированы три способа доступа к членам пространства имен из кода за его пределами.
namespace ContosoData < class ObjectManager < public: void DoSomething() <>>; void Func(ObjectManager) <> >
Использование полного имени:
ContosoData::ObjectManager mgr; mgr.DoSomething(); ContosoData::Func(mgr);
Чтобы добавить в область видимости один идентификатор, используйте объявление using:
using ContosoData::ObjectManager; ObjectManager mgr; mgr.DoSomething();
Чтобы добавить в область видимости все идентификаторы пространства имен, используйте директиву using:
using namespace ContosoData; ObjectManager mgr; mgr.DoSomething(); Func(mgr);
Директивы using
Директива using позволяет использовать все имена в объекте namespace без имени пространства имен в качестве явного квалификатора. Используйте директиву using в файле реализации (т. е. *.cpp), если используется несколько различных идентификаторов в пространстве имен; Если вы используете только один или два идентификатора, рассмотрите возможность использования объявления, чтобы использовать только эти идентификаторы в область, а не все идентификаторы в пространстве имен. Если локальная переменная имеет такое же имя, как и переменная пространства имен, то переменная пространства имен будет скрытой. Создавать переменную пространства имен с те же именем, что и у глобальной переменной, является ошибкой.
Директиву using можно поместить в верхнюю часть CPP-файла (в области видимости файла) или внутрь определения класса или функции.
Без особой необходимости не размещайте директивы using в файлах заголовков (*.h), так как любой файл, содержащий этот заголовок, добавит все идентификаторы пространства имен в область видимости, что может вызвать скрытие или конфликты имен, которые очень трудно отлаживать. В файлах заголовков всегда используйте полные имена. Если эти имена получаются слишком длинными, используйте псевдоним пространства имен для их сокращения. (См. ниже.)
Объявление пространств имен и их членов
Как правило, пространство имен объявляется в файле заголовка. Если реализации функций находятся в отдельном файле, определяйте имена функций полностью, как показано в следующем примере.
// contosoData.h #pragma once namespace ContosoDataServer
Реализации функций в contosodata.cpp должны использовать полное имя, даже если директива using размещается в верхней части файла:
#include "contosodata.h" using namespace ContosoDataServer; void ContosoDataServer::Foo() // use fully-qualified name here < // no qualification needed for Bar() Bar(); >int ContosoDataServer::Bar()
Пространство имен может быть объявлено в нескольких блоках в одном файле и в нескольких файлах. Компилятор соединит вместе все части во время предварительной обработки и полученное в результате пространство имен будет содержать все члены, объявленные во всех частях. Примером этого является пространство имен std, которое объявляется в каждом из файлов заголовка в стандартной библиотеке.
Члены именованного пространства имен можно определить вне пространства имен, в котором они объявлены явным определением имени. Однако определение должно располагаться после точки объявления в пространстве имен, окружающем то пространство имен, где находится объявление. Например:
// defining_namespace_members.cpp // C2039 expected namespace V < void f(); >void V::f() < >// ok void V::g() < >// C2039, g() is not yet a member of V namespace V
Эта ошибка может возникнуть, когда члены пространства имен объявляются в нескольких файлах заголовка и эти заголовки не включены в правильном порядке.
Глобальное пространство имен
Если идентификатор не объявлен явно в пространстве имен, он неявно считается входящим в глобальное пространство имен. Как правило, старайтесь избегать объявления в глобальном область, если это возможно, за исключением основной функции точки входа, которая должна находиться в глобальном пространстве имен. Чтобы явно указать глобальный идентификатор, используйте оператор разрешения области видимости без имени, как сделано в ::SomeFunction(x); . Это позволит отличать данный идентификатор от любого другого элемента с таким же именем, находящегося в другом пространстве имен. Кроме того, это облегчит понимание кода.
Пространство имен std
Все стандартные типы и функции библиотек C++ объявляются в std пространстве имен или пространствах имен, вложенных внутри std .
Вложенные пространства имен
Пространства имен могут быть вложенными. Обычное вложенное пространство имен имеет неквалифицированный доступ к членам родительского элемента, но родительские члены не имеют неквалифицированного доступа к вложенному пространству имен (если он не объявлен как встроенный), как показано в следующем примере:
namespace ContosoDataServer < void Foo(); namespace Details < int CountImpl; void Ban() < return Foo(); >> int Bar(); int Baz(int i) < return Details::CountImpl; >>
Обычные вложенные пространства имен можно использовать для инкапсуляции данных о внутренней реализации, которые не являются частью открытого интерфейса родительского пространства имен.
Встроенные пространства имен (C++11)
В отличие от обычных вложенных пространств имен члены встроенного пространства имен обрабатываются как члены родительского пространства имен. Эта особенность позволяет выполнять поиск перегруженных функций с зависимостью от аргументов среди функции, которые имеют перегрузки в родительском и вложенном встроенном пространстве имен. Это также позволяет объявлять специализации в родительском пространстве имен для шаблонов, объявленных во встроенном пространстве имен. В следующем примере показано, как внешний код привязывается к встроенному пространству имен по умолчанию.
// Header.h #include namespace Test < namespace old_ns < std::string Func() < return std::string("Hello from old"); >> inline namespace new_ns < std::string Func() < return std::string("Hello from new"); >> > // main.cpp #include "header.h" #include #include int main() < using namespace Test; using namespace std; string s = Func(); std::cout
В следующем примере показано, как можно объявить специализацию в родительском пространстве имен шаблона, объявленного во встроенном пространстве имен.
namespace Parent < inline namespace new_ns < template struct C < T member; >; > template<> class C <>; >
Встроенные пространства имен можно использовать как механизм управления версиями для управления изменениями в открытом интерфейсе библиотеки. Например, можно создать одно родительское пространство имен и инкапсулировать каждую версию интерфейса в своем собственном пространстве имен, вложенном в родительское. Пространство имен, которое содержит самую последнюю или основную версию, квалифицируется как встроенное и поэтому представляется так, будто оно является непосредственным членом родительского пространства имен. Клиентский код, вызывающий Parent::Class, автоматически привязывается к новому коду. Клиенты, которые предпочитают использовать старую версию, могут по-прежнему получить доступ к ней, используя полный путь к вложенному пространству имен, содержащему данный код.
Ключевое слово inline должно применяться к первому объявлению пространства имен в единице компиляции.
В следующем примере показано две версии интерфейса: каждое — во вложенном пространстве имен. Пространство имен v_20 содержит некоторые изменения из интерфейса v_10 и помечается как встроенное. Клиентский код, который использует новую библиотеку и вызывает Contoso::Funcs::Add , вызовет версию v_20. Код, который пытается вызвать Contoso::Funcs::Divide , теперь будет вызывать ошибку времени компиляции. Если действительно требуется эта функция, доступ к версии v_10 можно получить путем явного вызова Contoso::v_10::Funcs::Divide .
namespace Contoso < namespace v_10 < template class Funcs < public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); T Divide(T a, T b); >; > inline namespace v_20 < template class Funcs < public: Funcs(void); T Add(T a, T b); T Subtract(T a, T b); T Multiply(T a, T b); std::vectorLog(double); T Accumulate(std::vector nums); >; > >
Псевдонимы пространств имен
Имена пространств имен должны быть уникальными, из-за чего зачастую они получаются не слишком короткими. Если длина имени затрудняет чтение кода или утомительно вводить в файл заголовка, где нельзя использовать директивы, можно сделать псевдоним пространства имен, который служит сокращенным для фактического имени. Например:
namespace a_very_long_namespace_name < class Foo <>; > namespace AVLNN = a_very_long_namespace_name; void Bar(AVLNN::Foo foo)
анонимные или безымянные пространства имен
Вы можете создать явное пространство имен, но не присвоить ему имя.
namespace < int MyFunc()<>>
Это называется неименованным или анонимным пространством имен, и это полезно, если вы хотите сделать объявления переменных невидимыми для кода в других файлах (т. е. дать им внутреннюю компоновку), не создавая именованное пространство имен. Весь код, находящийся в том же файле, может видеть идентификаторы в безымянном пространстве имен, но эти идентификаторы, а также само пространство имен, будет невидимым за пределами этого файла или, точнее, вне блока перевода.
Пространство имен (using namespace std;)
Очень часто в интернете вижу как многие программисты усердно пишут везде программы используя в коде std:: . Зачем они это делают? Почему нельзя просто использовать using namespace std; перед программой, так же удобнее и код начинает "дышать". Или это плохой тон и стоит переучиваться на использование std:: непосредственно в коде программы?
Отслеживать
31.2k 13 13 золотых знаков 98 98 серебряных знаков 159 159 бронзовых знаков
задан 11 апр 2015 в 14:23
347 1 1 золотой знак 3 3 серебряных знака 6 6 бронзовых знаков
Добро пожаловать в мир крестов. Я не знаю как ответить на этот вопрос. По поводу дышашего кода из за одного using объявления вы преувеличиваете.
11 апр 2015 в 14:24
Так почему же преувеличиваю? Грубо говоря что я потратил всего одну строчку для using namespace std; и что у меня во всём коде можно сказать на каждой строчке красуется std. По факту очень даже ощутимая разница если визуально оценивать код. Может это конечно глупости и не стоит по поводу этого заморачиваться. Но пока что, этот момент мне не понятен)
11 апр 2015 в 14:28
Вы понимаете что в C например вообще нет пространств имен? А в С++11 директива using может быть использована например для создания шаблонных синонимов или вместо typedef для создания синонима типа. Короче я не знаю как ответить на вопрос почему какие-то люди где-то там далеко вместо using namepace std; пишут std::cout . Может им так нравится или они просто дураки? Откуда мне знать точную причину?
11 апр 2015 в 14:40
Вот ещё по теме: ru.stackoverflow.com/q/201310/10105
11 апр 2015 в 16:03
А что такое "дышащий код"?
26 апр 2015 в 6:31
5 ответов 5
Сортировка: Сброс на вариант по умолчанию
Зависит от традиций. Среди плюсовиков традиция "лучше перестраховаться, чем получить внезапные трудно отлаживаемые проблемы неизвестно где".
Явное указание пространства имён — это избавление от потенциальных проблем в будущем. Положим, вы подключили через using namespace два пространства имён. Всё замечательно, кратко, красиво.
А потом вышла новая версия одной из библиотек, и какие-то идентификаторы стали резолвиться по-другому, например, во второй библиотеке добавили функцию, которая к вашим аргументам подходит лучше, чем используемая вами ранее функция из первой библиотеки.
В лучшем случае ваш код не соберётся. Может упасть. А может так получиться, что ваш код перестанет работать у клиента в 1% случаев. Всё может быть.
Отлавливать и исправлять подобные проблемы мучительно больно.
Насколько это важно конкретно для вас — решать вам. Если у вас простой проектик и от силы пара сторонних библиотек (или вообще только стандартная библиотека), то можно не заморачиваться с явным указанием пространств имён. Если проект огромный, с десятками библиотек, то может оказаться более удобным (и наглядным) всегда указывать пространства имён.
Банальный пример: положим, вы пользуетесь только стандартной библиотекой и boost, поэтому решили везде писать:
using namespace std; using namespace boost;
. а теперь выходит новая версия стандартной библиотеки, в которой из boost перетащено много классов. И внезапно ваш код больше не компилируется.
В других языках другие традиции. Например, в C# почти всегда пишут краткие имена классов, и только в случае конфликтов явно указывают пространство имён или используют алиасы. Язык немного отличается: там нет функций вне классов. Это позволяет меньше терять читаемость и реже натыкаться на неожиданные конфликты.
Пространства namespace C# и C++
Во время разработки программного обеспечения иногда удается воспользоваться функциями, которые ранее были написаны другими программистами. Такие операции формируются в специальные библиотеки. Если она будет входить в состав языка или располагаться в открытом доступе, удастся задействовать все ее возможности.
Такая концепция удобна и практична. Некоторые создают универсальные библиотеки, которые будут пригодны для самых разных проектов. Здесь начинают фигурировать пространства имен. Далее предстоит разобрать их более подробно.
Определение
Пространство имен (namespaces) – группа взаимосвязанных функций, констант, переменных, объектов, а также иных компонентов программы. Набор знаков (имен), используемых для идентификации или ссылки на объекты тех или иных видов. Пространство выступает гарантом того, что все из заданного массива объектов обладают уникальными именами. Это помогает идентифицировать их в случае необходимости.
В C# классы и иные типы .NET не существуют сами по себе – их помещают в специальные контейнеры – пространства имен. С их помощью удается грамотно организовать код по логическим блокам. Пространства имен дают возможность объединения и отделения некоторой функциональности, связанной с общей идеей или решением той или иной задачи, от остальной части кода.
Name space поддерживают работу с:
- классами;
- интерфейсами;
- иными компонентами исходного программного кода.
С их помощью код становится более понятным и читабельным, разработка становится в несколько раз быстрее. Пространства имен используются преимущественно для классовой организации. Они дают возможность контроля области применения методов в более масштабных проектах.
Форма представления
Существуют различные пространства имен. Они определяются при помощи ключевого слова namespace. Задать соответствующий компонент для кода можно, пользуясь следующей формой:
За счет такого подхода удается сохранять одни наборы имен отличными от других. Данная концепция позволит устранить конфликты одинаковых имен классов между пространствами.
Членами здесь смогут выступить:
- делегаты;
- структуры;
- пространство имен.
Namespace может всегда характеризоваться в качестве группы классов с общими признаками.
Свойства namespace
Именные пространства обладают такими свойствами:
- разделение через оператор «.» (точку);
- организация крупных и сложных проектов при разработке программного обеспечения;
- global – это глобальное пространство имен.
Огромным спросом в рассматриваемом вопросе пользуется директива using. О ней будет рассказано более подробно позже. Сначала предстоит поближе изучить пространство имен с СИ-семействе.
Создание в C# – пример
В C# можно определить собственное namespace. Пример – в файле program.cs будет создано пространство под названием Base. Выглядит это так:
В примере, указанном выше, пространство имен имеет класс Person. В ней содержится одна переменная – name, а также конструктор и метод Print.
Далее можно попытаться воспользоваться классом Person:
В первой строке приведенного кода появится ошибка. Связано это с тем, что Visual Studio не сможет обнаружить класс Person. Для обращения к нему потребуется использовать полное имя этого класса. Во внимание обязательно принимаются имеющиеся пространства имен:
Соответствующий компонент расположен в пространстве Base. Его полным именем будет выступать Base.Person. С его помощью получится обратиться к Person вне его namespace name.
Подключение
Полное имя класса с учетом именного пространства будет делать код избыточным. Особенно тогда, когда оно включает в себя огромное количество classes, которые хочется использовать в процессе написания приложения.
Чтобы не писать полное имя класса, допускается подключение пространства имен. Для данной задачи будет применять директива using:
Далее using namespace std будет рассмотрена более подробно. Это очень важный компонент, без которого невозможно создавать читабельные крупные проекты.
Вложенные пространства
Существуют так называемые вложенные пространства имен в C# и С++. Это когда одни пространства включат в себя другие.
- Person и Company расположены в разных namespaces.
- Чтобы в классе Person можно было использовать класс Company, потребуется указать его name с учетом namespace: Organisation.Types.Company.
- Base можно не указывать. Связано это с тем, что оба класса в конечном итоге находятся в едином пространстве.
- В качестве альтернативы разрешается подключение пространства имен класса Company через директиву using.
Обратиться к соответствующим классам вне пространства Base получится, если использовать всю именную цепочку:
Выше – наглядный пример того, как это выглядит на практике.
Файлы заголовков
После версии .NET 6 и C# 10 появилась возможность определения пространства имен на уровне файлов. Для этого в проект в качестве примера рекомендуется добавить новый документ. Он будет включать в себя код C#:
- Запустить Visual Studio и открыть тот или иной проект.
- Нажать на название рабочего документа правой кнопкой мыши.
- Выбрать в появившемся навигационном меню Add-New Item.
- Кликнуть по Class и поле Name. В качестве имени выступит Base.cs.
- В проекте появится новый документ с указанным ранее именем. Из него потребуется удалить все записи.
- Вставить код: .
Директива namespace с name Base в самом начале документа указывает на то, что содержимое файла включает в себя именное пространство Base. Больше эта запись ни на что не ссылается.
Остается подключить пространство имен непосредственно в Program.cs. Выше – пример кода, который поможет сделать это корректно.
В C++
В C++ тоже можно создать файл заголовков. Он может быть перенесен в будущем в другие приложения для более быстрой разработки.
В качестве пространства имен будет использоваться такой код:
Здесь в группу namespace объединены несколько names. С помощью этого приема удается избежать именных конфликтов. Он пригодится, когда в какой-то из подключенных библиотек уже имеется функция. Пример – sum. По именной области программа поймет, что именно из namespace из одинаковых name необходима разработчику.
Для сокращения исходного кода может пригодиться директива Using. В данном случае фрагмент будет выглядеть так:
Команда указывает на то, что пользователю потребуются name из namespace с именами std и mynames.
После using допускается указание отдельной функции или переменной. Всю область пространства имен можно не прописывать:
Выше – наглядный пример того, как это сделать. В данном фрагменте из пространства имен подключается только функция sum().
Заголовки в C++ – пример
Ранее указанный фрагмент кода можно перенести в отдельный документ. Этот прием помогает подключать пространства имен к разным приложениям, избегая дополнительного кода в основном файле.
Потребуется создать файл заголовков. Выглядит программный код так:
Рекомендуется сохранить его с расширением .h. Пример – в документе mylib.h. В предложенном примере отсутствует функция main(). Это связано с тем, что предложенный код не является самостоятельным приложением. В точке входа здесь нет никакого смысла. Iostream тоже не подключается – ничего выводить при помощи блока кода не нужно. В заголовок для работы с пространствами имен можно добавлять совершенно разные файлы:
Далее необходимо разместить mylib.h в папке, где расположено «ключевое» приложение и добавить команду:
Стоит обратить внимание на то, что при записи пространства имен используются двойные кавычки. Они нужны для локальных заголовков. Если необходимо использовать системные, придется поставить угловые скобки.
Теперь пользователи смогут активировать все возможности имеющегося naming space в приложении:
А вот результат обработки кода:
Если необходимо использовать математические функции (pow и другие), потребуется подключение к библиотеке cmath.
Области видимости
Пространство имен – это область видимости. Оно может быть глобальным и локальным. В первом случае предстоит работать с глобальной именной областью. Вот пример приложения:
При попытке компиляции на экране появится ошибка. Связано это с тем, что глобальная область видимости не принимает более одной переменной с одним и тем же именем. Из-за этого внедряются «локальные» или «пользовательские» варианты.
Любая именная область должны быть объявлена в блоке. Соответствующий фрагмент кода выступает первой частью всех возможных распределенных областей пространства.
Объекты, объявленные в теле именной области – это его члены. Name в namespace – имена членов. Они будут введены объявлениями в декларативную именную область.
Стандартная область имен C++
В C++ имеется библиотека, называемая стандартной. В ней находятся названия:
- объектов;
- функций;
- иных сущностей языка.
Name здесь взяты из namespace std. Стандартная библиотека включает в себя подбиблиотеки. Наиболее популярная из них – iostream. Она поддерживает объект named cout. Он применяется для того, чтобы отправлять на консоль или терминал результаты обработки кода.
Чтобы воспользоваться стандартной библиотекой в C++, потребуется указать в начале кода такую запись:
Рекомендуется обратить внимание на применение директивы using и std. Include – это директива препроцессора. Она не должна заканчиваться точкой с запятой. Поддерживает в своем составе файл iostream в позиции своей директивы.
Несколько слов о using
Директива using – средство импорта именных областей. Избавляет программиста от полной классификации стереотипных названий. Позволяет использовать определенные типы. Указывать полное пространство не придется. В базовой форме using происходит импорт всех типов именных областей.
Имеет поддержку нескольких модификаторов:
- Global. Работает так, как и при добавлении директивы use к каждому исходному документу в проекте.
- Static. Импортирует компоненты static, а также вложенные типы из одного выбранного, а не из всего пространства имен.
Пишется using в самом начале корда – до пространственных имен. В противном случае возникнет ошибка компиляции.
Здесь можно увидеть больше информации о рассмотренной теме. Чтобы быстрее разобраться с int main и иными вопросами именных областей, рекомендуется закончить специализированные дистанционные компьютерные онлайн курсы.
Пространства имен в C++
В данной статье я бы хотел рассмотреть такую важную тему как пространства имен. Данная возможность языка широко используется как разработчики самого языка C++, так и представителями компаний, которые разрабатывают современное ПО.
Что такое пространство имен?
Для начала, давайте попробуем понять для чего вообще нужны эти самые пространства имен.
Предположим, у вас есть крупный проект который содержит огромное количество классов и переменных. Естественно над ним не будет работать один человек, а значит возможны ошибки из-за, например, создание второго класса с именем Employee (ведь программист может и не зна