Почему включение «использования пространства имен» в заголовочный файл - плохая идея в C ++? - PullRequest
26 голосов
/ 02 февраля 2011

Читая из "Мышления в C ++" Брюса Экеля о пространствах имен, я столкнулся со следующим утверждением:

Однако вы практически никогда не увидите использование директивы в заголовочном файле (по крайней мере, не за пределами области видимости). причина в том, что с помощью директивы устранить защиту этого конкретное пространство имен и эффект последний до конца текущего блок компиляции. Если вы положили использование директива (вне области видимости) в заголовочный файл, это означает, что эта потеря «защита пространства имен» будет происходить в пределах любой файл, который включает это заголовок, который часто означает другой заголовок файлы.

Не могли бы вы помочь мне понять приведенное выше утверждение с помощью простого примера?

Ответы [ 4 ]

30 голосов
/ 02 февраля 2011

Рассмотрим эту программу:

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 :: значительно уменьшается.

16 голосов
/ 02 февраля 2011

Если заголовок содержит using namespace std, все из этого пространства имен добавляется глобальное пространство имен в каждый модуль, который включает заголовок.

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

5 голосов
/ 22 июня 2013

Скопируйте следующий абзац из «Учебника по С ++, пятое издание»:

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

4 голосов
/ 02 февраля 2011

Хорошо, какой смысл использовать пространства имен. Это чтобы избежать риска столкновения имен.

Допустим, у вас есть довольно распространенное имя класса, например, FooBar. Если вы используете несколько библиотек, существует риск того, что FooBar в библиотеке A столкнется с FooBar в библиотеке B. Для этого мы используем два разных пространства имен A и B, чтобы переместить FooBars из глобального пространства имен в A :: FooBar и B :: FooBar (поэтому они хранятся отдельно друг от друга).

Если вы затем поместите using A; и using B; в заголовки, то это переместит A :: FooBar и B :: FooBar просто в FooBar, вернув коллизию, в первую очередь удалив усиление от использования пространств имен.

...