Урок №53. Пространства имен

  Юрий  | 

  |

  Обновл. 4 Сен 2021  | 

 100806

 ǀ   20 

Этот урок является продолжением урока №24.

Конфликт имен

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

Рассмотрим пример такого конфликта. boo.h и doo.h — это заголовочные файлы с функциями, которые выполняют разные вещи, но имеют одинаковые имена и параметры.

boo.h:

doo.h:

main.cpp:

Если boo.h и doo.h скомпилировать отдельно, то всё пройдет без инцидентов. Однако, соединив их в одной программе, мы подключим две разные функции, но с одинаковыми именами и параметрами, в одну область видимости (глобальную), а это, в свою очередь, приведет к конфликту имен. В результате, компилятор выдаст ошибку. Для решения подобных проблем и добавили в язык С++ такую концепцию, как пространства имен.

Что такое пространство имен?


Пространство имен определяет область кода, в которой гарантируется уникальность всех идентификаторов. По умолчанию, глобальные переменные и обычные функции определены в глобальном пространстве имен. Например:

Глобальная переменная g_z и функция boo() определены в глобальном пространстве имен.

В примере, приведенном выше, при подключении файлов boo.h и doo.h обе версии doOperation() были включены в глобальное пространство имен, из-за чего, собственно, и произошел конфликт имен.

Чтобы избежать подобных ситуаций, когда два независимых объекта имеют идентификаторы, которые могут конфликтовать друг с другом при совместном использовании, язык C++ позволяет объявлять собственные пространства имен через ключевое слово namespace. Всё, что объявлено внутри пользовательского пространства имен, — принадлежит только этому пространству имен (а не глобальному).

Перепишем заголовочные файлы из вышеприведенного примера, но уже с использованием namespace:

boo.h:

doo.h:

Теперь doOperation() из файла boo.h находится в пространстве имен Boo, а doOperation() из файла doo.h — в пространстве имен Doo. Посмотрим, что произойдет при перекомпиляции main.cpp:

Результатом будет еще одна ошибка:

C:\VCProjects\Test.cpp(15) : error C2065: 'doOperation' : undeclared identifier

Случилось так, что когда мы попытались вызвать функцию doOperation(), компилятор заглянул в глобальное пространство имен в поисках определения doOperation(). Однако, поскольку ни одна из наших версий doOperation() не находится в глобальном пространстве имен, компилятор не смог найти определение doOperation() вообще!

Существует два разных способа сообщить компилятору, какую версию doOperation() следует использовать: через оператор разрешения области видимости или с помощью using-стейтментов (о них мы поговорим на следующем уроке).

Доступ к пространству имен через оператор разрешения области видимости (::)

Первый способ указать компилятору искать идентификатор в определенном пространстве имен — это использовать название необходимого пространства имен вместе с оператором разрешения области видимости (::) и требуемым идентификатором.

Например, сообщим компилятору использовать версию doOperation() из пространства имен Boo:

Результат:

9

Если бы мы захотели использовать версию doOperation() из пространства имен Doo:

Результат:

1

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

Результат:

9
1

Также этот оператор можно использовать без какого-либо префикса (например, ::doOperation). В таком случае мы ссылаемся на глобальное пространство имен.

Пространства имен с одинаковыми названиями


Допускается объявление пространств имен в нескольких местах (либо в нескольких файлах, либо в нескольких местах внутри одного файла). Всё, что находится внутри одного блока имен, считается частью только этого блока.

add.h:

subtract.h:

main.cpp:

Всё работает, как нужно.

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

Псевдонимы и вложенные пространства имен

Одни пространства имен могут быть вложены в другие пространства имен. Например:

Обратите внимание, поскольку Doo находится внутри Boo, то доступ к g_x осуществляется через Boo::Doo::g_x.

Так как это не всегда удобно и эффективно, то C++ позволяет создавать псевдонимы для пространств имен:

Стоит отметить, что пространства имен в C++ не были разработаны, как способ реализации информационной иерархии — они были разработаны в качестве механизма предотвращения возникновения конфликтов имен. Как доказательство этому, вся Стандартная библиотека шаблонов находится в единственном пространстве имен std::.

Вложенность пространств имен не рекомендуется использовать, так как при неумелом использовании увеличивается вероятность возникновения ошибок и дополнительно усложняется логика программы.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (595 оценок, среднее: 4,94 из 5)
Загрузка...

Комментариев: 20

  1. cybersatori:

    добавлю что если используете вижуал студию, то необходимо тестировать код через консольное приложение иначе возникает ошибка линкёра, мало ли кто тоже столкнётся.

    1. stalker572:

      Эм… кажется он среди 33 моих ошибок увидел ещё и ошибку пространств имен… Поэтому я здесь…

    2. Борис:

      Хорошо что не "ошибка линкора" 🙂

  2. Вася:

    Почему тут не используется Header guards или #pragma once? Это является необязательным?

    1. Никита:

      1. они обязательны
      2. вы где так хедеры увидели? как по мне там только cpp-шники

  3. Александр:

    Из урока 21 раздела Советы: "Не определяйте функции в заголовочных файлах".

    Из текущего урока, в заголовочных файлах boo.h и doo.h ОПРЕДЕЛЕНЫ ФУНКЦИИ.

    Так где правда-то?

    1. Денис:

      Правда в том, что это просто пример. Не стал автор писать отдельно срр файл для функции.

    2. Fantasy:

      Суть в том что наши std (namespase) и команды что следуют после std:: всего лишь ссылки на действие в библиотеке. Т-е саму функцию мы написали в заголовочном файле чтоб превратить в пространство имен.

  4. Константин:

    Уважаемый Юра! Подскажи, пожалуйста, как выводить на экран название той переменной, которую инициализирует пользователь в процессе выполнения программы (run-time)? Пример: файлы GeoConClay.cpp и похожие содержат такой код:

    файлы GeoConClay.h и похожие содержат такой код:

    файл zvit.cpp содержит фрагмент такого кода:

    При запуске программы в консольном окне ввожу (с бумажки) данные, а при выводе они оказываются присвоенными не в те переменные…

    1. Константин:

      Ага! Путём эмпирического подбора разобрался в очерёдности присвоения значений пользователем в константные переменные!!!

  5. Андрей:

    Что делать если, в 2-х разных dll (от разных разработчиков) имена пространства имен одинаковые и нет исходного кода?
    Компилятор пишет: "ошибка: C2757: GenApi: символ с этим именем уже существует, поэтому оно не может быть использовано в качестве имени пространства имен"

  6. Константин:

    Юра, скажи, пожалуйста, освоив твои труды, можно ли написать некий код, который, подключив к AutoCad (или какой-то другой "рисовалке") , заставит его начертить некие простые геометрические фигуры или надо самому сидеть пялиться в монитор и орудовать мышкой? (я, собственно, и начал изучать С++ чтобы это сделать:-)

    1. Фото аватара Юрий:

      Можно. Это ведь великий и могучий C++. Начертить фигуры можно и с помощью Visual Studio, не обязательно подключать AutoCad. Но сам принцип написания такой программы предполагает, что вы указываете координаты фигуры (т.е. как она рисуется), а затем уже можете добавить возможность изменения углов, длины стороны, радиуса, диаметра и т.д. и фигура будет нарисована автоматически с указанными параметрами.

      1. Константин:

        И с помощью Code::Blocks (которым я пользуюсь) можно начертить?

        1. Фото аватара Юрий:

          Code::Blocks не использовал, поэтому не могу сказать точно.

        2. Артемий:

          CodeBlocks местами гибче… Мне даже больше нравится чем VS

  7. Константин:

    Юрий, Ваши труды спасают меня от депрессии:-) В этом уроке мне не понятно появление void в скобках после main. Растолкуйте, пожалуйста.

    1. Фото аватара Юрий:

      Спасибо, что читаете 🙂 void указано здесь для того, чтобы сообщить, что функция main не принимает никаких параметров.

      1. Константин:

        То есть именно в данном фрагменте void употреблён как пережиток языка С. а суть дела не изменилась — не впущу никаких параметров и баста! Я правильно понял?

        1. Фото аватара Юрий:

          Да, впринципе в С++ void не обязательно указывать, можно просто пустые скобки, но в первых версиях языка эта практика применялась. И да, это всё пошло от языка С.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *