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

  Юрий  | 

    | 

  Обновл. 18 Апр 2019  | 

 8578

 ǀ   11 

Этот урок является продолжением урока №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 (109 оценок, среднее: 4,94 из 5)
Загрузка...

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

  1. Аватар Константин:

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

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

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

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

    1. Аватар Константин:

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

  2. Аватар Андрей:

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

  3. Аватар Константин:

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

    1. Юрий Юрий:

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

      1. Аватар Константин:

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

        1. Юрий Юрий:

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

  4. Аватар Константин:

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

    1. Юрий Юрий:

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

      1. Аватар Константин:

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

        1. Юрий Юрий:

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

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

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