Namespace std что содержит
Пространства имен (namespaces) в C++
Пространство имен в C ++ — это обобщенная область видимости. Его объявление начинается с зарезервированного слова namespace, за которым следует имя по выбору программиста, а затем блок в фигурных скобках. Блок содержит базовые объявления и / или определения объектов, функций и других сущностей C ++.
Рассмотрим следующие два скалярных оператора в глобальной области в следующей программе:
int varId = 5 ;
float varId = 2.3 ;
Попытка скомпилировать эту программу приводит к ошибке компиляции. Есть две переменные с одинаковым именем varId. Хотя это две разные переменные двух разных типов, int и float, компилятор отклоняет два объявления, потому что они имеют одно и то же имя. Следующая программа решает эту проблему, объявляя переменные с одинаковыми именами в двух разных обобщенных областях:
namespace NA
<
int varId = 5 ;
>
namespace NB
<
float varId = 2.3 ;
>
int main ( )
<
cout NA :: varId ‘ \n ‘ ;
cout NB :: varId ‘ \n ‘ ;
Результат выглядит следующим образом:
В этой статье рассматривается основная концепция пространства имен и его использование в языке программирования C ++. Чтобы следовать этой статье, вы должны иметь базовые знания языка C ++. Вы также должны знать область действия C ++, хотя она кратко объясняется в этой статье. Чтобы узнать больше о области действия C ++, найдите фразу «Область действия в C ++» (без кавычек) в поле поиска любой веб-страницы linuxhint.com и нажмите Enter. Это приведет вас к статье, написанной этим автором.
Что такое пространство имен?
Декларативная область — это самая большая часть программы, в которой допустимо имя объекта (переменной). Эта область называется областью действия. Пространство имен в C ++ — это обобщенная область видимости, основной целью которой является разрешение конфликтов имен. Пространство имен имеет базовые объявления и / или определения сущностей.
Глобальное пространство имен и его проблема
Глобальное пространство имен — это глобальная область видимости. Рассмотрим следующую короткую программу:
int ident = 55 ;
float ident = 12.17 ;
В приведенной выше программе есть две переменные, обе называемые идентификатором. Эти переменные находятся в глобальной области видимости; то есть они находятся в глобальном пространстве имен. Попытка скомпилировать эту программу завершится ошибкой. Глобальная область видимости не принимает более одной переменной с одинаковым именем, поэтому существует необходимость в настраиваемом пространстве имен.
Пользовательское пространство имен
namespace NA
<
int varInt = 6 ;
float flt ;
>
namespace NB
<
int varInt = 7 ;
float flt ;
>
int main ( )
<
cout NA :: varInt ‘ \n ‘ ;
cout NB :: varInt ‘ \n ‘ ;
NA :: flt = 2.5 ;
NB :: flt = 4.8 ;
cout NA :: flt ‘ \n ‘ ;
cout NB :: flt ‘ \n ‘ ;
Обратите внимание, что имена NA :: flt и NB :: flt в конечном итоге определены в функции main (). C ++ не допускает такого определения в глобальной области видимости.
Обратите внимание, что настраиваемое пространство имен является вложенным пространством имен для глобального пространства имен.
Директива использования
Чтобы не вводить все время «namepace :: name» вместо просто «name» после объявления пространства имен, вы можете использовать директиву using. Синтаксис использования директивы using следующий:
С помощью директивы не директива препроцессора, поэтому она заканчивается точкой с запятой (;).
Следующая программа иллюстрирует использование директивы using и др.:
namespace NB
<
int varInt = 7 ;
int func ( )
<
return varInt ;
>
>
int fn ( )
<
using namespace NB ;
int myVar2 = func ( ) ;
//other objects and functions from NB follow.
return myVar2 ;
>
int myVar3 = NB :: func ( ) ;
int main ( )
<
cout fn ( ) ‘ ‘ myVar3 ‘ \n ‘ ;
Переменная, объявленная в глобальной области (глобальном пространстве имен), просматривается от точки объявления до конца файла. Это также видно во вложенных пространствах имен (вложенных областях), таких как вложенная область видимости функции fn () выше. С помощью директивы соединяет его пространство имен из позиции, в которой он размещен в конце области, в которой он размещен.
Имя func () из пространства имен NB нельзя увидеть под определением fn (), потому что » using namespace NB;» был помещен в область действия функции (блок). При этом условии, чтобы использовать функцию » func () » вне блока (области) пространства имен NB, ему должен предшествовать » NB :: «, как в следующем операторе:
С помощью директивы присоединяется своим пространством имен с внешним гнездовым пространством именами из положения, в котором он находится на конец внешних вложенности имен. В следующей программе пространство имен NA объединено с глобальным пространством имен. Оба пространства имен затем расширяются в пространство имен определения функции fn (), в котором они объединяются с пространством имен NB. Пространство имен NB заканчивается в конце определения функции fn (), а два предыдущих пространства имен продолжаются до конца файла (считывания кода).
namespace NA
<
int varInt = 6 ;
int func ( )
<
return varInt ;
>
namespace NB
<
int varInt = 7 ;
int func ( )
<
return varInt ;
>
>
using namespace NA ;
int myVar0 = varInt ;
//other objects and functions from :: and NB follow.
int fn ( )
<
int myVar1 = varInt ;
using namespace NB ;
int myVar2 = NB :: func ( ) ;
//other objects and functions from NB follow, till end of this scope.
return myVar1 + myVar2 ;
>
//Only objects and functions from :: and NB follow.
int myVar3 = NB :: func ( ) ;
int main ( )
<
cout myVar0 ‘ ‘ fn ( ) ‘ ‘ myVar3 ‘ \n ‘ ;
На выходе будет 6, 13, 7.
Под утверждением » using namespace NA; «Переменные из глобального пространства имен и пространства имен NA могут использоваться без указания их исходного пространства имен. Следующий оператор использует varInt пространства имен NA. Область объединенного пространства имен global и NA простирается в пространство имен функции fn (). Итак, varInt первого оператора в области видимости функции fn () относится к пространству имен NA.
Поскольку область для глобального пространства имен и пространства имен NA распространяется на всю область видимости fn (), после » int myVar2 = NB :: func ();, «Любое имя из пространства имен NB может использоваться только в области fn () без предшествующего ему» NB :: «, только если оно не встречается в NA и глобальных пространствах имен (блоках). В противном случае ему должно предшествовать » NB :: «. Область объединенных пространств имен для NA и global продолжается ниже определения fn () и в функцию main () до конца файла.
Расширение пространства имен NB начинается с » int myVar2 = NB :: func (); «В блоке fn () и заканчивается в конце блока определения fn ().
Примечание: Пространства имен, регионы которых соединяются, не должны иметь одинаковые имена переменных в разных блоках пространств имен, так как это все равно вызовет конфликт.
Области пространства имен
Пространство имен — это область видимости. Помимо глобального пространства имен (глобальная область видимости), любое пространство имен должно быть объявлено в блоке. Этот блок является первой частью возможных распределенных областей пространства имен. С помощью директивы using пространство имен может быть расширено как регионы в других областях.
Объекты, объявленные в теле пространства имен, называются членами этого пространства имен, а имена, введенные этими объявлениями в декларативную область пространства имен, называются именами членов этого пространства имен.
Вложенные пространства имен
Следующая программа показывает вложенные пространства имен:
namespace A
<
int i = 1 ;
namespace B
<
int i = 2 ;
namespace C
<
int i = 3 ;
>
>
>
int main ( )
<
cout A :: i ‘ ‘ A :: B :: i ‘ ‘ A :: B :: C :: i ‘ \n ‘ ;
Обратите внимание, что доступ к трем значениям был осуществлен с помощью оператора разрешения области видимости.
Стандартное пространство имен
В C ++ есть библиотека, называемая стандартной библиотекой. Имена объектов, функций и других сущностей в этой библиотеке взяты из пространства имен, называемого стандартным пространством имен, записанного как std. Стандартная библиотека содержит подбиблиотеки, и одна из этих подбиблиотек — iostream. Библиотека iostream содержит объект cout, который используется для отправки результатов на консоль (терминал).
Имя cout должно находиться в пространстве имен std. Чтобы использовать iostream с его пространством имен std, программа должна быть следующей:
Обратите внимание на использование директивы using и std. Термин » #include » является директивой препроцессора и не заканчивается точкой с запятой. Он включает в себя «файл» iostream в позиции своей директивы.
Заключение
Пространство имен — это область видимости. Описание (определение) пространства имен содержит базовые объявления и / или определения объектов, функций и других сущностей C ++. Вне определения пространства имен доступ к имени можно получить с помощью синтаксиса » namespaceName :: name «. Помимо глобального пространства имен (глобальная область видимости), любое пространство имен должно быть объявлено в блоке. Этот блок является первой частью возможных распределенных областей пространства имен. С помощью директивы using пространство имен может быть расширено как регионы в других областях. Пространства имен, регионы которых соединяются, не должны иметь одинаковые имена переменных в разных блоках пространств имен, так как это все равно вызовет конфликт имен.
Изучаем C++. Часть 8. Библиотеки и пространства имён
Разбираемся, как ускорить работу с кодом в несколько раз, используя готовые решения.
Это восьмая часть из серии статей «Глубокое погружение в C++». В прошлой статье мы узнали, как хранить данные в массивах. Сегодня — ещё интереснее.
Недавно мы говорили о том, что многие функции уже написаны другими разработчиками и помещены в специальные библиотеки. Если такая библиотека входит в состав языка или находится в открытом доступе, то вы можете использовать все её возможности.
Это очень удобно, и многие специально пишут универсальные библиотеки, которые пригодятся в самых разных проектах. Давайте разберёмся с библиотеками подробнее и научимся создавать свои.
Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.
Пространства имён в C++
Пространство имён (англ. namespace) — это группа взаимосвязанных функций, переменных, констант, классов, объектов и других компонентов программы.
С самого начала изучения C++ мы используем команду std: cout, чтобы выводить данные в терминал. На самом деле команда называется просто cout, а std — это пространство имён, в котором она находится.
Пространства имён нужны, чтобы логически связывать части программы. Например, математические функции, физические, бухгалтерские и так далее.
Вот пример создания пространства имён:
Мы объединяем в группу несколько разных команд и избегаем конфликтов имён. Это нужно, когда в какой-то из подключённых вами библиотек уже есть функция, например sum (). По пространству имён программа поймёт, какая именно функция вам нужна.
Если же вы хотите сократить код, то используйте команду using:
В данном случае команда говорит, что вам нужны имена из mynames и std, поэтому никакой ошибки выведено не будет.
Также после using можно указать не целое пространство имён, а только отдельную функцию или переменную:
Файлы заголовков в C++
Пространство имён из примера выше можно перенести в отдельный файл, чтобы потом подключить его к другой программе и избавиться от дополнительного кода в основном файле.
Здесь нет функции main (), потому что этот код — не самостоятельная программа, а библиотека для других программ. Следовательно, точка входа здесь не нужна. Также мы не подключаем iostream, потому что не собираемся ничего выводить, но вы можете добавить в свой заголовок любые другие файлы.
Поместите mylib.h в папку, где находится ваша программа. Затем добавьте в начало кода команду:
Обратите внимание на двойные кавычки вместо угловых скобок: первые используются для локальных заголовков, а вторые — для системных.
Теперь вы можете использовать весь функционал из этой библиотеки:
Вот что будет выведено:
Обратите внимание, что функция pow, как и другие математические функции, существует также и в библиотеке cmath.
Заключение
Библиотеки и пространства имён — это полезные инструменты для каждого разработчика. В интернете есть готовые решения для любых задач, поэтому многие работодатели ищут специалистов, которые разбираются в определённой библиотеке.
Если вы часто работаете над однотипными проектами, можете написать для себя библиотеку и подключать её во все проекты, чтобы ускорить работу. Однако новичкам стоит стараться писать код каждый раз заново — так вы сможете его постоянно переосмысливать и замечать то, чего раньше не замечали.
Если вы хотите освоить С++, то можете пройти наш курс. В нём все аспекты языка разбираются детально и на практике, а в конце каждого задания вы получаете обратную связь от преподавателей.
Урок №53. Пространства имен
Этот урок является продолжением урока №24.
Конфликт имен
Конфликт имен возникает, когда два одинаковых идентификатора находятся в одной области видимости, и компилятор не может понять, какой из этих двух следует использовать в конкретной ситуации. Компилятор или линкер выдаст ошибку, так как у них недостаточно информации, чтобы решить эту неоднозначность. Как только программы увеличиваются в объемах, количество идентификаторов также увеличивается, следовательно, увеличивается и вероятность возникновения конфликтов имен.
Рассмотрим пример такого конфликта. boo.h и doo.h — это заголовочные файлы с функциями, которые выполняют разные вещи, но имеют одинаковые имена и параметры.
Если boo.h и doo.h скомпилировать отдельно, то всё пройдет без инцидентов. Однако, соединив их в одной программе, мы подключим две разные функции, но с одинаковыми именами и параметрами, в одну область видимости (глобальную), а это, в свою очередь, приведет к конфликту имен. В результате, компилятор выдаст ошибку. Для решения подобных проблем и добавили в язык С++ такую концепцию, как пространства имен.
Что такое пространство имен?
Пространство имен определяет область кода, в которой гарантируется уникальность всех идентификаторов. По умолчанию, глобальные переменные и обычные функции определены в глобальном пространстве имен. Например:
Глобальная переменная g_z и функция boo() определены в глобальном пространстве имен.
В примере, приведенном выше, при подключении файлов boo.h и doo.h обе версии doOperation() были включены в глобальное пространство имен, из-за чего, собственно, и произошел конфликт имен.
Чтобы избежать подобных ситуаций, когда два независимых объекта имеют идентификаторы, которые могут конфликтовать друг с другом при совместном использовании, язык C++ позволяет объявлять собственные пространства имен через ключевое слово namespace. Всё, что объявлено внутри пользовательского пространства имен, — принадлежит только этому пространству имен (а не глобальному).
Перепишем заголовочные файлы из вышеприведенного примера, но уже с использованием namespace:
Результатом будет еще одна ошибка:
C:\VCProjects\Test.cpp(15) : error C2065: ‘doOperation’ : undeclared identifier
Случилось так, что когда мы попытались вызвать функцию doOperation(), компилятор заглянул в глобальное пространство имен в поисках определения doOperation(). Однако, поскольку ни одна из наших версий doOperation() не находится в глобальном пространстве имен, компилятор не смог найти определение doOperation() вообще!
Существует два разных способа сообщить компилятору, какую версию doOperation() следует использовать: через оператор разрешения области видимости или с помощью using-стейтментов (о них мы поговорим на следующем уроке).
Доступ к пространству имен через оператор разрешения области видимости (::)
Первый способ указать компилятору искать идентификатор в определенном пространстве имен — это использовать название необходимого пространства имен вместе с оператором разрешения области видимости ( :: ) и требуемым идентификатором.
Например, сообщим компилятору использовать версию doOperation() из пространства имен Boo :
6.2 – Пользовательские пространства имен
В уроке «2.8 – Конфликты имен и пространства имен» мы познакомились с концепциями конфликтов имен и пространств имен. Напоминаем, что конфликт имен возникает, когда два идентичных идентификатора вводятся в одну и ту же область видимости, и компилятор не может однозначно определить, какой из них использовать. Когда это происходит, компилятор или компоновщик выдаст ошибку, потому что у них недостаточно информации для разрешения неоднозначности. По мере того, как программы становятся больше, количество идентификаторов увеличивается линейно, что, в свою очередь, приводит к экспоненциальному увеличению вероятности возникновения конфликта имен.
Давайте еще раз рассмотрим пример конфликта имен, а затем покажем, как мы можем разрешить его с помощью пространств имен. В следующем примере foo.cpp и goo.cpp – это исходные файлы, содержащие функции, выполняющие разные действия, но имеющие одинаковое имя и параметры.
Определение ваших собственных пространств имен
Идентификаторы пространства имен обычно пишутся без заглавных букв.
Ниже приведен пример файлов из предыдущего примера, переписанных с использованием пространств имен:
А ответ – теперь мы получаем еще одну ошибку!
В этом случае компилятор был удовлетворен (нашим предварительным объявлением), но компоновщик не смог найти определение для doSomething в глобальном пространстве имен. Это потому, что обе наши версии doSomething больше не находятся в глобальном пространстве имен!
Есть два разных способа сообщить компилятору, какую версию doSomething() использовать, с помощью оператора разрешения области видимости или с помощью инструкций using (которые мы обсудим на следующем уроке этой главы).
Для удобства чтения в последующих примерах мы объединим предыдущие примеры в однофайловый проект.
Доступ к пространству имен с помощью оператора разрешения области видимости ( :: )
Лучший способ указать компилятору искать идентификатор в определенном пространстве имен – это использовать оператор разрешения области видимости ( :: ). Оператор разрешения области видимости сообщает компилятору, что идентификатор, указанный правым операндом, следует искать в области видимости левого операнда.
Это дает ожидаемый результат:
Эта программа дает следующий результат:
Оператор разрешения области видимости хорош, потому что он позволяет нам явно выбирать, в каком пространстве имен мы хотим искать, и поэтому нет потенциальной неоднозначности. Мы даже можем сделать следующее:
Эта программа дает следующий результат:
Использование оператора разрешения области видимости без префикса имени
Оператор разрешения области видимости также может использоваться перед идентификатором без указания имени пространства имен (например, ::doSomething ). В таком случае идентификатор (например, doSomething ) ищется в глобальном пространстве имен.
В приведенном выше примере ::print() работает так же, как если бы мы вызвали print() без разрешения области видимости, поэтому использование оператора разрешения области видимости в этом случае излишне. Но в следующем примере будет показан случай, когда оператор разрешения области видимости без указания пространства имен может быть полезен.
Разрешение идентификатора из пространства имен
Если идентификатор используется внутри пространства имен, и разрешение области видимости не предусмотрено, компилятор сначала попытается найти соответствующее объявление в том же пространстве имен. Если соответствующий идентификатор не найден, компилятор затем последовательно проверяет каждое содержащее его пространство имен, чтобы увидеть, найдено ли совпадение, причем глобальное пространство имен проверяется последним.
Эта программа напечатает:
Если бы foo::print() не была найден, компилятор проверил бы содержащее его пространство имен (в данном случае глобальное пространство имен), чтобы увидеть, можно ли найти print() там.
Допускается несколько блоков пространства имен
Допускается объявлять блоки пространства имен в нескольких местах (либо в нескольких файлах, либо в нескольких местах в одном файле). Все объявления в пространстве имен считаются частью одного пространства имен.
Это работает именно так, как вы ожидаете:
Предупреждение
Когда вы разделяете свой код на несколько файлов, вам нужно будет использовать пространство имен в заголовочном и исходном файлах.
Вложенные пространства имен
Пространства имен могут быть вложены в другие пространства имен. Например:
Начиная с C++17, вложенные пространства имен также могут быть объявлены следующим образом:
Псевдонимы пространств имен
Поскольку ввод полного имени переменной или функции во вложенном пространстве имен может быть болезненным, C++ позволяет создавать псевдонимы пространств имен, которые позволяют нам временно сократить длинную последовательность пространств имен до чего-то более короткого:
Стоит отметить, что пространства имен в C++ изначально были разработаны не как способ реализации информационной иерархии – они были разработаны, в первую очередь, как механизм предотвращения конфликтов имен. В качестве доказательства этого обратите внимание, что вся стандартная библиотека находится в одном пространстве имен std:: (с некоторыми вложенными пространствами имен, используемыми для новых функций библиотеки). Некоторые новые языки (например, C#) в этом отношении отличаются от C++.
В общем, вам следует избегать глубоких вложений пространств имен.
Когда следует использовать пространства имен
В приложениях пространства имен могут использоваться для отделения кода, специфичного для приложения, от кода, который может быть повторно использован позже (например, математические функции). Например, физические и математические функции могут входить в одно пространство имен (например, math:: ). Функции языка и локализации – в другое пространство имен (например, lang:: ).
Когда вы пишете библиотеку или код, который хотите распространить среди других, всегда помещайте свой код в пространство имен. Код, в котором используется ваша библиотека, может не соответствовать лучшим практикам – в таком случае, если объявления вашей библиотеки не находятся в пространстве имен, существует высокая вероятность возникновения конфликтов имен. В качестве дополнительного преимущества, размещение кода библиотеки внутри пространства имен также позволяет пользователю видеть содержимое вашей библиотеки с помощью функции автодополнения и предложений редактора кода.
Почему с ‘using namespace std;’ в *.cpp-файлах может быть очень плохо
То, что написано ниже, для многих квалифицированных C++ разработчиков будет прекрасно известным и очевидным, но тем не менее, я периодически встречаю using namespace std; в коде различных проектов, а недавно в нашумевшей статье про впечатления от высшего образования было упомянуто, что студентов так учат писать код в вузах, что и сподвигло меня написать эту заметку.
Для чего вообще придумали пространства имен в C++? Когда какие-то две сущности (типы, функции, и т.д.) имеют идентификаторы, которые могут конфликтовать друг с другом при совместном использовании, C++ позволяет объявлять пространства с помощью ключевого слова namespace. Всё, что объявлено внутри пространства имен, принадлежит только этому пространству имен (а не глобальному). Используя using мы вытаскиваем сущности какого-либо пространства имен в глобальный контекст.
А теперь посмотрим, к чему это может привести.
Допустим, вы используете две библиотеки под названием Foo и Bar и написали в начале файла что-то типа
. таким образом вытащив всё, что есть в foo:: и в bar:: в глобальное пространство имен.
Все работает нормально, и вы можете без проблем вызвать Blah() из Foo и Quux() из Bar. Но однажды вы обновляете библиотеку Foo до новой версии Foo 2.0, которая теперь еще имеет в себе функцию Quux().
Теперь у вас конфликт: и Foo 2.0, и Bar импортируют Quux() в ваше глобальное пространство имен. В лучшем случае это вызовет ошибку на этапе компиляции, и исправление этого потребует усилий и времени.
А вот если бы вы явно указывали в коде метод с его пространством имен, а именно, foo::Blah() и bar::Quux(), то добавление foo::Quux() не было бы проблемой.
Но всё может быть даже хуже!
В библиотеку Foo 2.0 могла быть добавлена функция foo::Quux(), про которую компилятор по ряду причин посчитает, что она однозначно лучше подходит для некоторых ваших вызовов Quux(), чем bar::Quux(), вызывавшаяся в вашем коде на протяжении многих лет. Тогда ваш код все равно скомпилируется, но будет молча вызывать неправильную функцию и делать бог весть что. И это может привести к куче неожиданных и сложноотлаживающихся ошибок.
Имейте в виду, что пространство имен std:: имеет множество идентификаторов, многие из которых являются очень распространенными (list, sort, string, iterator, swap), которые, скорее всего, могут появиться и в другом коде, либо наоборот, в следущей версии стандарта C++ в std добавят что-то, что совпадет с каким-то из идентификаторов в вашем существующем коде.
Если вы считаете это маловероятным, то посмотрим на реальные примеры со stackoverflow:
Вот тут был задан вопрос о том, почему код возвращает совершенно не те результаты, что от него ожидает разработчик. По факту там происходит именно описанное выше: разработчик передает в функцию аргументы неправильного типа, но это не вызывает ошибку компиляции, а компилятор просто молча использует вместо объявленной выше функции distance() библиотечную функцию std::distance() из std:: ставшего глобальным неймспейсом.
Второй пример на ту же тему: вместо функции swap() используется std::swap(). Опять же, никакой ошибки компиляции, а просто неправильный результат работы.
Так что подобное происходит гораздо чаще, чем кажется.