У всех, наверное, студентов, начавших изучать C++, в первые же дни возникает вопрос об этом "странном" using namespace std: что это такое, почему одни пишут так, а другие вот так?
namespace — это пространство имён
C++ — это такой язык программирования, в котором очень широко используют различные подключаемые файлы. Даже чтобы просто вывести текст на экран, люди подключают, например, или файл iostream, или cstdio (наследие языка Си). В таких файлах широко применяются глобальные сущности. Одним файлом всё не ограничивается, подключают файлы очень часто, а написаны эти файлы очень разными программистами, друг о друге часто даже не знающими. Так как программисты часто не знают друг о друге и не связаны друг с другом никакими отношениями, то и в кодах их могут оказаться одноимённые сущности.
C++
1
2
#include "FileWasa"; //Один файл
#include "FilePetr"; //Второй файл
Пётр и Вася написали два каких-то кода, в которых оказались два одноимённых класса.
Чтобы это написать, нужно знать, как писать программы, разделённые на файлы.
Если попробовать собрать проект из показанных кодов, скомпилировать программу и попробовать её запустить, то будет неудача, этот пример не компилируется. Возникает проблема: повторение имени. Нельзя давать одинаковые имена разным сущностям. Директива #include — это указание компилятору вписать текст из указанного файла в текущее место кода. Формально выполняется обычная подстановка текстов обоих файлов в основную программу. Происходит разворачивание в один кусок текста:
Код основной программы при компилировании выглядит вот так:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*include на развороте*/
#ifndef FILEPETR_H_INCLUDED
#define FILEPETR_H_INCLUDED
classMyClass{
};
#endif // FILEPETR_H_INCLUDED
#ifndef FILEWASA_H_INCLUDED
#define FILEWASA_H_INCLUDED
classMyClass{
};
#endif // FILEWASA_H_INCLUDED
/*А это то, что было изначально*/
intmain(){
}
MyClass объявлен дважды, что и порождает проблему.
Самый очевидная борьба с таким проявлением недоразумения — давать очень длинные имена названиям, используя всевозможные приставки, суффиксы и что-нибудь ещё. Есть только маленькое но — применение такого способа не только не даёт гарантий, что не случится несовпадения, но и громоздит код.
В С++ возможно избежать такой проблемы достаточно простым способом. Можно создать что-то наподобие срезов глобальной области. Эти срезы с одной стороны относятся к глобальной области, с другой стороны глобальными областями не являются. Можно сказать, что глобальное пространство разбивается на границы. Получаются глобальные участки. Я надеюсь, что вы поняли то, о чём я успел написать. Дальше идёт вторая ступенька этой темы.
А вот сейчас тот самый момент, который является причиной НЕиспользования такого способа. Несмотря на то, что пространства имён различны, включение второго пространства имён может привести к тому, от чего так старательно уходили создатели языка, придумывая решение именно этой проблемы:
C++
1
2
3
4
5
6
7
8
9
#include "FilePetr.h"
#include "FileWasa.h"
usingnamespaceWasa;
usingnamespacePetr;//Я нарушитель спокойствия
intmain(){
MyClassx;
}
Этот последний код просто откажется компилироваться, именно потому что дважды объявлен класс MyClass. Здесь происходит обратный эффект: изначально внутри файлов глобальная территория была поделена на независимые территории, а внутри основной программы буквально говорят: давай мне все территории сразу, они мне все нужны. В итоге случается недоразумение, которое выше было описано. Жадность — начало местной беды.
Именно по этой причине использование формы using namespace std; так сильно прессуется опытными программистами. Ведь программист, который пишет другой код, не обязан знать обо всём, что написано в пространстве имён std или в файлах Петра и Васи. Теперь, полагаю, читателю легче понять, что существует большой риск повторения имён.
Даже вот в таком виде, проблема остаётся:
C++
1
2
3
4
5
6
7
8
9
#include "FilePetr.h"
#include "FileWasa.h"
usingWasa::MyClass;
usingPetr::MyClass;//Проблема осталась
intmain(){
MyClassx;
}
Именно поэтому чаще всего всё сводится к узкой локализации в конкретном, в нужном месте:
C++
1
2
3
4
5
6
7
8
#include "FilePetr.h"
#include "FileWasa.h"
intmain(){
Wasa::MyClassx;//Прямое описание
Petr::MyClassy;//Прямое описание
}
//Всё замечательно работает
Вот такая банальная причина нужна, чтобы протестовать против общеупотребимой новичками формы. Да, это быстро, удобно писать, код читабельней выглядит, но в то же время такая манера "жадного" написания кода даёт эффект описанной ошибки.
Если кода изначально написано много, а в нём использован "жадный способ" то при добавлении ещё одного файла к проекту дописывать для каждого места то, что изначально было опущено, может оказаться не самым приятным делом. Сам я не программист, поэтому не имел с этим практики, но последняя мысль моя вряд ли далека от истинны. Хотя, конечно, может я ошибся в таком выводе.
Речь немного о другом: чтобы избежать случайного включения какого-либо объявления, лучше использовать прямые описания.
Жадный способ — это способ, который употребим новичками: using namespace std; Жадным назван только мной, потому что буквально он и есть жадный: когда глобальная сущность поделена на кусочки, этот способ норовит взять всю глобальную сущность: "и для кого резали?" — спрашивается. Это несуществующий сейчас термин, моего авторства, не специалистов.
Но в подавляющем большинстве случаев лучше сразу привыкать к прямому описанию. На этом сайте в основном используется "жадная форма". Это для удобочитаемости, но не потому что это хорошо.
Ещё одна причина некоторой нелюбви к непрямому указанию пространства имени — это возможное перекрытие переменной.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
namespacena//пространство имен na
{
chara[]="na::a";
voidf();
}
namespacenb//пространство имён nb
{
chara[]="nb::b";
voidf();
}
chara[]="::a";//глобальная переменная
voidna::f(){std::cout<<a<<'\n';}//функции
voidnb::f(){std::cout<<a<<'\n';}
intmain()
{
na::f();//вызовы функций
nb::f();
}
Вот в таком, например, примере всё работает. Но есть один момент, здесь объявлена глобальная переменная: символьный массив а, инициализированный значением ::a. Ведь автор кода мог справедливо надеяться на то, что внутри его функций будет задействована общая глобальная переменная со значением ::a. А внутри функций, неожиданно для него, задействуются переменные из пространств имён. Это такой вид ошибок, когда всё работает, но что-то идёт не так, самый противный и нелюбимый всеми. Очень сложно словить такие ошибки в программах.
Здесь кода мало и программа маленькая, поэтому и исправить всё это очень легко, нужно использовать прямое обращение к глобальному пространству.
Вот пример более полного кода на самостоятельный разбор. Лучше этот код разбирать комментируя участки кода в main. Я поделил эти участки на группы по три строки, один такой блок закомментировал.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <iostream>
constchar*constpch1="\n===============\n\n\n";
constchar*constpch2="===============\n";
voidfoo();
namespacena
{
constchara[]="na::a";
voidf();
}
namespacenb
{
constchara[]="nb::b";
voidf();
}
constchara[]="::a";
voidna::f()
{
std::cout<<"a = "<<a<<'\n';
std::cout<<"a = "<<::a;//обращение к глобальной переменной
}
voidnb::f()
{
std::cout<<"a = "<<a<<'\n';
std::cout<<"a = "<<::a;//обращение к глобальной переменной
}
voidfoo()
{
// std::cout << a; //я не скомпилируюсь, конфликт имён
std::cout<<"a = "<<::a<<'\n';//обращение к глобальной переменной
std::cout<<"a = "<<nb::a<<'\n';
std::cout<<"a = "<<na::a;
}
intmain()
{
std::cout<<"OUTPUT na::f():\n"<<pch2;
na::f();//<-------ВЫЗОВ 1
std::cout<<pch1;
std::cout<<"OUTPUT nb::f():\n"<<pch2;
nb::f();//<-------ВЫЗОВ 2
std::cout<<pch1;
/*
std::cout << "OUTPUT foo():\n" << pch2;
foo(); //<-------ВЫЗОВ 3
std::cout << pch1;
*/
}
Это пример показывает не только почему использование не самого прямого пространства имён плохо/ненадёжно, но и вообще вскрывает некоторую проблему глобальных имён. Использование пространств имён с явным прописыванием способствует написанию кода без особых головных болей. Как видно в примере, даже для глобальной переменной иногда имеет смысл использовать явное указание пространства имён, в функции foo() строка кода закомментирована, где конфликт имён. Как этот пример представить проще я не придумал, поэтому смотрите на его работу, комментируя блоки в main, и вникая в происходящее.
Мне остаётся надеяться, что эта тема была интересной и помогла разобраться в каких-то моментах. Используйте прямые обращения к пространствам имён, и, возможно, вы избавите себя от трудноуловимой ошибки.
Один комментарий на «“Немножко о using namespace std: почему ругают и чем плохо”»
пипос, место того, чтобы ты расписал побольше как что и где, ты расписал за что тебя могут прессинговать коллеги по работе или кто там, какие у вас норм пасанские правила и что это и что то жзначит про тебя как про человека, жаден ты или что хе
пипос, место того, чтобы ты расписал побольше как что и где, ты расписал за что тебя могут прессинговать коллеги по работе или кто там, какие у вас норм пасанские правила и что это и что то жзначит про тебя как про человека, жаден ты или что хе
это называется свехнутся и нерубить суть.
читал с удовольствием суть ты донёс спс