Using namespace std c что это

от admin

What does ‘using namespace std’ mean in C++?

Have you ever wondered why do we put ‘using namespace std’ on the top of our code in C++. What is namespace?, Is it really necessary to mention this, like we mention header files in our code. In this article we will try to get the answer of these questions.

First, lets understand what a“namespace” is!

What is namespace?

It is a container for a set of identifiers. It provide a level of direction to specific identifiers, thus making it possible to distinguish between identifiers with the same exact name. For example, a surname could be thought of as a namespace that makes it possible to distinguish people who have the same given name. For further details go to this link.

Why we need namespaces?

Assume there is two libraries named A and B, both have get_data function in it. You included these two libraries in your code. Now you call the get_data function, then compiler won’t know which one to call, and you’ll get an error. Now how we can fix this issue. In C lang we can fix by adding a prefix to start of every function like A_get_data and B_get_data,but is painful. The solution of this problem comes in C++ in the form of namespace. It is more flexible way of adding prefixes. So now you can call the functions as A::get_data and B::get_data where :: is scope resolution operator.

Now come to point, what does ‘using namespace std’ do.

Why we mention this in our code?

Now suppose in the above example, if you are going to use bunch of code of A library then its going to boring to add prefix every time while calling the functions of A library. Then what is the solution of this problem

here is the solution you can add “using namespace A” at the top of your file, and then just call get_data without using A prefix for the rest of code.

Is ‘using namespace std’ is necessary to mention?

The answer is big NO.

The std namespace is special, The built in C++ library routines are kept in the standard namespace. That includes stuff like cout, cin, string, vector, map, etc. Because these tools are used so commonly, it’s popular to add “using namespace std” at the top of your source code so that you won’t have to type the std:: prefix constantly. It only make our task easy, It is not not necessary.

Drawbacks of ‘using namespace’

Professional programmers considered that it is bad manners to put “using namespace” declarations in header files. It forces all includers of that header file to use that namespace, which might result in naming ambiguities that are hard to fix. This practice is called namespace pollution. Instead, always use the fully prefixed names in header files (std::string not string) and save the using declarations for the source files.

Пространство имен (using namespace std;)

Очень часто в интернете вижу как многие программисты усердно пишут везде программы используя в коде std:: . Зачем они это делают? Почему нельзя просто использовать using namespace std; перед программой, так же удобнее и код начинает «дышать». Или это плохой тон и стоит переучиваться на использование std:: непосредственно в коде программы?

Зависит от традиций. Среди плюсовиков традиция «лучше перестраховаться, чем получить внезапные трудно отлаживаемые проблемы неизвестно где».

Явное указание пространства имён — это избавление от потенциальных проблем в будущем. Положим, вы подключили через using namespace два пространства имён. Всё замечательно, кратко, красиво.

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

В лучшем случае ваш код не соберётся. Может упасть. А может так получиться, что ваш код перестанет работать у клиента в 1% случаев. Всё может быть.

Отлавливать и исправлять подобные проблемы мучительно больно.

Насколько это важно конкретно для вас — решать вам. Если у вас простой проектик и от силы пара сторонних библиотек (или вообще только стандартная библиотека), то можно не заморачиваться с явным указанием пространств имён. Если проект огромный, с десятками библиотек, то может оказаться более удобным (и наглядным) всегда указывать пространства имён.

Банальный пример: положим, вы пользуетесь только стандартной библиотекой и boost, поэтому решили везде писать:

. а теперь выходит новая версия стандартной библиотеки, в которой из boost перетащено много классов. И внезапно ваш код больше не компилируется.

В других языках другие традиции. Например, в C# почти всегда пишут краткие имена классов, и только в случае конфликтов явно указывают пространство имён или используют алиасы. Язык немного отличается: там нет функций вне классов. Это позволяет меньше терять читаемость и реже натыкаться на неожиданные конфликты.

6.12 – Объявления using и директивы using

Вы, наверное, видели эту программу во многих учебниках и учебных руководствах:

Некоторые старые компиляторы также начинают новые проекты с аналогичной программы.

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

Краткий урок истории

Еще до того, как C++ получил поддержку пространств имен, все имена, которые сейчас присутствуют в пространстве имен std , были в глобальном пространстве имен. Это вызывало конфликты имен между идентификаторами программ и идентификаторами стандартной библиотеки. Программы, которые работали с одной версией C++, могли получить конфликт имен с более новой версией C++.

В 1995 году пространства имен были стандартизированы, и все функции стандартной библиотеки были перенесены из глобального пространства имен в пространство имен std . Это изменение сломало старый код, в котором всё еще использовались имена без std:: .

Любой, кто работал с большой кодовой базой, знает, что любое изменение кодовой базы (каким бы тривиальным оно ни было) может нарушить работу программы. Обновление каждого имени, которое теперь было перемещено в пространство имен std для использования префикса std:: , было огромным риском. Было запрошено решение.

Перенесемся в сегодняшний день – если вы много используете стандартную библиотеку, набирание std:: перед всем, что вы используете из стандартной библиотеки, может стать повторяющейся рутиной, а в некоторых случаях может затруднить чтение вашего кода.

C++ предлагает решение обеих этих проблем в виде инструкций using .

Но сначала давайте определим два термина.

Полные и неполные имена

Имя может быть полным или неполным.

Полное имя – это имя, которое включает в себя связанную область видимости. Чаще всего имена дополняются пространством имен с помощью оператора разрешения области видимости ( :: ). Например:

Для продвинутых читателей

Имя также может быть уточнено именем класса с помощью оператора разрешения области ( :: ) или объектом класса с помощью операторов выбора члена ( . или -> ). Например:

Неполное имя – это имя, которое не включает в себя квалификатор области видимости. Например, cout и x являются неполными именами, поскольку они не включают связанную область видимости.

Объявления using

Один из способов уменьшить повторение ввода std:: снова и снова – использовать инструкцию объявления using . Объявление using позволяет нам использовать неполное имя (без области видимости) в качестве псевдонима для полного имени.

Вот наша базовая программа Hello world , в которой используется объявление using в строке 5:

Объявление using с использованием std::cout ; сообщает компилятору, что мы собираемся использовать объект cout из пространства имен std . Поэтому всякий раз, когда он видит cout , он предполагает, что мы имеем в виду std::cout . Если существует конфликт имен между std::cout и каким-либо другим использованием cout , предпочтительнее использовать std::cout . Поэтому в строке 6 мы можем ввести cout вместо std::cout .

В этом тривиальном примере это не сэкономит много усилий, но если вы много раз используете cout внутри функции, объявление using может сделать ваш код более читабельным. Обратите внимание, что вам потребуется отдельное объявление using для каждого имени (например, одно для std::cout , одно для std::cin и т.д.).

Хотя этот метод менее явный, чем использование префикса std:: , он обычно считается безопасным и приемлемым (при использовании внутри функции).

Директивы using

Другой способ упростить эту задачу – использовать директиву using . Директива using импортирует все идентификаторы из пространства имен в область видимости директивы using .

Вот снова наша программа Hello World с директивой using в строке 5:

Директива using namespace std; указывает компилятору импортировать все имена из пространства имен std в текущую область видимости (в данном случае в функцию main() ). Затем, когда мы используем неполный идентификатор cout , он будет преобразован в импортированный std::cout .

Читать:
Как сохранить в фл студио трек в формате мп3

Директивы using – это решение, которое было предоставлено для баз старого кода до пространства имен, которые использовали неполные имена для функций стандартной библиотеки. Вместо того, чтобы вручную обновлять каждое неполное имя до полного имени (что было рискованно), можно было бы разместить одну директиву using (т.е. using namespace std; ) в верхней части каждого файла, и все имена, которые были перемещены в пространство имен std всё еще можно было использовать неполными.

Проблемы с директивами using

Однако для современного кода C++ директивы using дают немного выгоды (экономия на вводе текста) по сравнению с риском.

Поскольку директивы using импортируют все имена из пространства имен (потенциально включая множество имен, которые вы никогда не будете использовать), вероятность возникновения конфликтов имен значительно возрастает (особенно, если вы импортируете пространство имен std ).

Для наглядности рассмотрим пример, в котором директивы using вызывают неоднозначность:

В приведенном выше примере компилятор не может определить, относится ли x в main к a::x или b::x . В этом случае код не будет скомпилирован из-за ошибки «ambiguous symbol» (неоднозначный символ). Мы могли бы решить эту проблему, удалив одну из инструкций using , используя вместо нее объявление using или определив x с явным квалификатором области видимости ( a:: или b:: ).

Вот еще один более коварный пример:

В приведенном выше примере компилятор не может определить, наше использование cout означает std::cout или функцию cout , которую мы определили, и снова не сможет выполнить компиляцию из-за ошибки «неоднозначного символа». Хотя этот пример тривиален, если бы у нас был явный префикс std::cout , например:

или использовалось объявление using вместо директивы using :

тогда наша программа вообще не имела бы никаких проблем.

Даже если директивы using не вызывают конфликтов имен сегодня, они делают код более уязвимым для будущих конфликтов. Например, если ваш код включает директиву using для какой-либо библиотеки, которая затем обновляется, все новые имена, представленные в обновленной библиотеке, теперь являются кандидатами на конфликты имен с вашим существующим кодом.

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

Рассмотрим следующую программу:

Эта программа запускается и печатает 1 .

Теперь предположим, что мы обновляем библиотеку foolib , которая включает обновленный foolib.h . Наша программа теперь выглядит так:

Наш файл main.cpp вообще не изменился, но теперь эта программа запускается и выводит 2 !

Когда компилятор встречает вызов функции, он должен определить, с каким определением функции он должен сопоставить этот вызов. При выборе функции из набора потенциально совпадающих функций он предпочтет ту функцию, которая не требует преобразования аргументов, а не ту, которая требует преобразования аргументов. Поскольку литерал 0 принадлежит целочисленному типу, C++ предпочтет сопоставить someFcn(0) с недавно добавленной someFcn(int) (без преобразований), а не с someFcn(double) (требуется преобразование из int в double). Это вызывает неожиданное изменение результатов работы нашей программы.

Этого бы не произошло, если бы мы использовали объявление using или явный квалификатор области видимости.

Область видимости объявлений и директив using

Если объявление using или директива using используется в блоке, имена применимы только в этом блоке (они следуют обычным правилам области видимости блока). Это хорошо, поскольку снижает вероятность возникновения конфликтов имен внутри этого блока.

Если объявление using или директива using используются в глобальном пространстве имен, имена применимы ко всему остальному файлу (они имеют область видимости файла).

Отмена или замена инструкции using

После объявления инструкции using ее невозможно отменить или заменить ее другой инструкцией using в той области видимости, в которой она была объявлена.

Лучшее, что вы можете сделать, – это намеренно с самого начала ограничить область видимости инструкции using с помощью правил области видимости блока.

Конечно, всей этой головной боли можно избежать, в первую очередь, используя явным образом оператор разрешения области видимости ( :: ).

Лучшие практики для инструкций using

В современном C++ на самом деле нет места для использования директив. Они увеличивают вероятность коллизий имен сейчас и в будущем и могут вызывать более коварные проблемы. Хотя во многих учебниках и руководствах они широко используются, использования директив лучше вообще избегать.

Объявления using обычно считаются безопасными для использования внутри блоков. Ограничьте их использование в глобальном пространстве имен файлов исходного кода и никогда не используйте их в глобальном пространстве имен заголовочных файлов.

Лучшая практика

Предпочитайте явное указание пространств имен вместо инструкций using . Полностью избегайте директив using . Внутри блоков допускается использование объявлений using .

Почему с ‘using namespace std;’ в *.cpp-файлах может быть очень плохо

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

Итак. многие слышали, что using namespace std; в начале файла в C++ считается плохой практикой и нередко даже явно запрещен в принятых во многих проектах стандартах кодирования. Касательно недопустимости использования using namespace в header-файлах вопросов обычно не возникает, если мы хоть немного понимаем, как работает препроцессор компилятора: .hpp-файлы при использовании директивы #include вставляются в код «как есть», и соответственно using автоматически распространится на все затронутые .hpp- и .cpp-файлы, если файл с ним был заинклюден хоть в одном звене цепочки (на одном из сайтов это метко обозвали «заболеванием передающимся половым путем«). Но вот про .cpp-файлы все не так очевидно, так что давайте еще раз разберем, что же именно здесь не так.

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

Похожие статьи