Почему включение "использование пространства имен" в файл заголовка является плохой идеей в C++?
читая "мышление на C++" Брюса Экеля о пространствах имен, я столкнулся со следующим утверждением:
вы практически никогда не увидите использование директивы в заголовочном файле (по крайней мере, не вне сферы действия). Этот причина в том, что использование директивы устранить охрану конкретное пространство имен, и эффект продлится до конца текущего единица компиляции. Если вы положили использование директива (вне сферы действия) в заголовочный файл, это значит что это потеря "защита пространства имен" будет происходить внутри любой файл, который включает это заголовок, который часто означает другой заголовок файлы.
не могли бы вы помочь мне понять приведенное выше утверждение с помощью простого примера?
4 ответов
считайте эту программу:
line#
1 #include <string>
2
3 using namespace std;
4
5 struct string { const char* p; }; // Beware: another string!
6
7 int main()
8 {
9 string x; // Error: ambiguous - which string is wanted?
10 }
если вы попытаетесь скомпилировать его, вы увидите ошибки:
g++ using.cc -o using
using.cc: In function `int main()':
using.cc:9: error: use of `string' is ambiguous
using.cc:5: error: first declared as `struct string' here
/usr/lib/gcc/i386-redhat-linux/3.4.6/../../../../include/c++/3.4.6/bits/stringfwd.h:60: error:
also declared as `typedef struct std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::string' here
using.cc:9: error: `string' was not declared in this scope
using.cc:9: error: expected `;' before "x"
проблема здесь в том, что когда main()
указывает string x;
, компилятор не уверен, определил ли пользователь ::string
или включены std::string
находится в розыске.
теперь представьте, что вы берете верхнюю часть программы... строки с 1 по 5 - до struct string
... и поместить его в файл заголовка, который вы затем #include
до main()
. Ничего изменения: у вас все еще есть ошибка. Итак, как и для автономных программ, заголовочные файлы с using
операторы в них могут вызвать проблемы для другого кода, который включает их, что делает некоторые из их заявлений неоднозначными.
это может быть большая боль, хотя, поскольку заголовки могут быть включены-прямо или косвенно - сколь угодно большим количеством зависимого кода, и...
- удаление
using
утверждение из заголовка, или - изменение содержимого из
<string>
, или любой другой заголовок, затрагивающихstd::
...может нарушить код, включая проблемный заголовок. Любая из этих проблем может сделать зависимый код некомпилируемым, и проблемы могут даже не быть замечены, пока не будет предпринята другая компиляция. Далее, Человек, страдающий из-за using
оператор может не иметь разрешений файловой системы / кода-репозитория, корпоративных полномочий и т. д. чтобы удалить using
оператор из заголовка, ни исправить другой затронутый клиент код.
тем не менее, если заголовок имеет только "использование" внутри класса или функции, то нет никакого влияния на код за пределами этой области, поэтому потенциальное влияние изменений на std:: резко уменьшается.
Если заголовок содержит using namespace std
, все из этого пространства имен добавляется глобальное пространство имен в каждом модуле, который включает заголовок.
Это означает, что вы никогда не можете объявить функцию или определить класс с тем же именем (и совместимыми параметрами для функции) в качестве std
функцию/класс в глобальном пространстве имен, в любом из этих модулей.
скопируйте следующий абзац из "C++ Primer, fifth edition":
код внутри заголовков обычно не должны использовать
using
декларации. Этот причина в том, что содержимое заголовка копируется в including текст программы. Если заголовок имеет объявление using, то каждый программа, включающая этот заголовок, получает то же самое с помощью объявления. Как результат, программа, которая не намеревалась использовать указанную библиотеку имя может столкнуться с неожиданным именем рознь.
Ну, в чем смысл использования пространств имен. Это делается для того, чтобы избежать риска коллизий имен.
предположим, что у вас есть довольно распространенное имя класса, например FooBar. Если вы используете несколько библиотек, есть риск, что фрагментик в библиотеке сталкивается с foobar в библиотеке В. Для этого мы используем два разных пространства имен A и B, чтобы переместить FooBars из глобального пространства имен для::foobar и Б::фрагментик (так они хранятся отдельно друг от друга).
Если вы затем положите using A;
и using B;
в заголовках это переместит A:: FooBar и B:: FooBar в просто FooBar, вернув столкновение, удалив выигрыш от использования пространств имен в первую очередь.