"использование пространства имен" в заголовках c++


во всех наших курсах c++, все учителя всегда ставят using namespace std; сразу после #includeв их .h файлы. Это кажется мне опасным с тех пор, включив этот заголовок в другую программу, я получу пространство имен, импортированное в мою программу, возможно, не осознавая, намереваясь или желая его (включение заголовка может быть очень глубоко вложенным).

так что мой вопрос двойной: я прав, что using namespace не следует использовать в заголовочных файлах, и/или есть ли способ, чтобы отменить это, что-то вроде:

//header.h
using namespace std {
.
.
.
}

еще один вопрос в том же духе: должен ли заголовочный файл #include все заголовки, что он соответствует .cpp файл нуждается, только те, которые необходимы для определения заголовка и пусть .cpp file #include остальные, или нет и объявить все, что ему нужно, как extern?
Аргументация вопроса такая же, как и выше: я не хочу сюрпризов при включении .h файлы.

кроме того, если я прав, это распространенная ошибка? Я имею в виду в реальном программировании и в "реальных" проектах там.

спасибо.

9 103

9 ответов:

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

заголовок должен включите только заголовки, которые ему нужно скомпилировать. Простой способ обеспечить это-всегда включать собственный заголовок каждого исходного файла в качестве первого, перед любыми другими заголовками. Тогда исходный файл не будет компилироваться, если заголовок не является автономным. В некоторых случаях, например со ссылкой на классы сведений о реализации в библиотеке, можно использовать прямые объявления вместо #include потому что у вас есть полный контроль над определением такого прямого объявленного класса.

Я не уверен, что я бы назвал его общим, но он определенно появляется время от времени, обычно написанный новыми программистами, которые не знают о негативных последствиях. Как правило, только небольшое образование о рисках заботится о любых проблемах, так как это относительно просто исправить.

пункт 59 В Саттере и Александреску "стандарты кодирования C++: 101 правила, рекомендации и лучшие практики":

  1. не записывайте использование пространства имен в заголовочный файл или перед #include. 108

названия всех руководящих принципов находятся в http://www.gotw.ca/publications/c++cs.htm, но детали должны быть прочитаны для разработчиков C++.

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

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

Что касается пространств имен, я обычно использую явную область пространства имен в моих заголовочных файлах и только помещаю using namespace в моих файлах cpp.

Проверьте стандарты кодирования Центра космических полетов Годдарда (для C и c++). Это оказывается немного сложнее, чем раньше-см. обновленные ответы на вопросы SO:

стандарт кодирования GSFC C++ гласит:

§каждый заголовочный файл должен 3.3.7 #include файлы, которые необходимо скомпилировать, вместо того, чтобы заставлять пользователей #include необходимые файлы. #includes должно быть ограничено тем, что нужно заголовку; другое #includes должен быть помещен в исходный файл.

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

Вы правы, что using namespace в заголовке опасно. Я не знаю, как это исправить. Это легко обнаружить, однако просто искать using namespace в заголовочных файлах. По этой последней причине он редко встречается в реальных проектах. Более опытные сотрудники скоро будут жаловаться, если кто-то делает что-то подобное.

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

вы правы. И любой файл должен включать только заголовки, необходимые для этого файла. Что касается "делает ли что-то неправильно в реальных проектах?- о да!

Как и все вещи в программировании, прагматизм должен победить догматизм, ИМО.

пока вы принимаете решение по всему проекту ("Наш проект широко использует STL, и мы не хотим добавлять все с помощью std::."), Я не вижу в этом проблемы. Единственное, чем вы рискуете, это столкновение имен, в конце концов, и с повсеместностью STL это вряд ли будет проблемой.

с другой стороны, если это было решение одного разработчика в одном (non-private) header-file, я вижу, как это вызовет путаницу среди команды и ее следует избегать.

Я считаю, что вы можете использовать "using" в заголовках C++ безопасно, если вы пишете свои объявления во вложенном пространстве имен следующим образом:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Это должно включать только вещи, объявленные в 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' без используемых пространств имен. Я протестировал его на компиляторе mingw64.

что касается "Есть ли способ отменить [a using декларации]?"

Я думаю, что полезно указать, что using объявления зависят от области видимости.

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

так эффективно, да. Ограничивая область действия using объявление его эффект длится только в пределах этой области; он "отменяется", когда эта область заканчивается.

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

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