"Використання простору імен" у заголовках c ++


119

На всіх наших курсах c ++ всі викладачі завжди кладуть using namespace std;відразу після #includes у свої .hфайли. Мені це здається небезпечним з того часу, включивши цей заголовок в іншу програму, я отримаю імпортоване ім’я до моєї програми, можливо, не усвідомлюючи, не маючи намір чи бажаючи цього (включення заголовка може бути дуже глибоко вкладеним).

Отже, моє запитання подвійне: чи я правий, що using namespaceйого не слід використовувати у файлах заголовків, та / чи є якийсь спосіб його скасувати, щось на кшталт:

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

Ще одне запитання в цих же рядках: Чи повинен заголовок містити #includeвсі заголовки, яким .cppпотрібен відповідний файл, лише ті, які потрібні для визначення заголовка, і дозволити .cppфайлу #includeрешту, або жоден і оголосити все, що йому потрібно extern?
Міркування, що стоїть за запитом, те саме, що вище: Я не хочу сюрпризів, коли включати .hфайли.

Також, якщо я маю рацію, чи це звичайна помилка? Я маю на увазі в реальному програмуванні та в «реальних» проектах там.

Дякую.



3
як бічна примітка, якщо у вас виникають зіткнення з іменами через using namespaceзаяви, ви можете використати повноцінне ім’я для вирішення проблеми.
Маріус Банчіла

Відповіді:


115

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

Заголовок повинен містити лише заголовки, які йому потрібно скласти. Найпростіший спосіб застосувати це - завжди включати власний заголовок кожного вихідного файлу як перше, перш ніж будь-які інші заголовки. Тоді вихідний файл не вдасться зібрати, якщо заголовок не є самостійним. У деяких випадках, наприклад, посилаючись на клас деталізації реалізації в бібліотеці, ви можете використовувати декларації вперед, а не #includeчерез те, що ви маєте повний контроль над визначенням такого попереднього оголошеного класу.

Я не впевнений, що я б назвав це звичайним, але воно, безумовно, з’являється раз у раз, як правило, написане новими програмістами, які не знають про негативні наслідки. Зазвичай лише незначна освіта про ризики вирішує будь-які проблеми, оскільки це виправити порівняно просто.


2
чи ми можемо використовувати usingзаяви у своїх .cppфайлах? в 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators є смертю до кінчиків пальців.
Крістофер

1
і як слід упорядкувати templateфункції - які повинні бути в заголовках? typedefs?
Крістофер

1
@donlan, здається, ви не отримали відповіді довгий час ... Так, ви можете використовувати usingоператори у .cppфайлах без особливого занепокоєння, оскільки область дії буде обмежена лише цим файлом, але ніколи не робіть цього перед #includeзаявою. Щодо функцій шаблону, визначених у заголовках, на жаль, я не знаю хорошого рішення, окрім того, як просто виписати простір імен ... Можливо, ви могли б поставити usingдекларацію в окремий діапазон { /* using statement in between brackets */ }, що принаймні не дозволить їй уникнути поточного файлу .
tjwrona1992

26

Пункт 59 у "Стандартах кодування C ++: Саттер та Олександреску ": 101 правила, вказівки та найкращі практики " :

59. Не записуйте вживання простору імен у файл заголовка або перед #include.

Простір імен usingдля зручності, а не для того, щоб наносити іншим: Ніколи не пишіть usingдекларацію чи usingдирективу перед #includeдирективою.

Висновок: у файлах заголовка не пишіть usingдирективи або usingдекларації на рівні простору імен ; натомість, чітко простір імен - кваліфікуйте всі імена.

Файл заголовка є гостем в одному або декількох вихідних файлах. Файл заголовка, що включає usingдирективи та декларації, також привертає своїх непорочних друзів.

using Декларація приносить приятель. using Директива приносить у всіх приятелів в просторі імен. Використання вашими вчителями using namespace std;директиви щодо використання.

Більш серйозно, у нас є простори імен, щоб уникнути зіткнення імен. Файл заголовка призначений для забезпечення інтерфейсу. Більшість заголовків є агностичними щодо того, який код може включати їх, зараз чи в майбутньому. Додавання usingвисловлювань для внутрішньої зручності всередині заголовка перетворює ці зручні імена на всіх потенційних клієнтів цього заголовка. Це може призвести до зіткнення імен. І це просто грубість.


12

Вам потрібно бути обережними, включаючи заголовки всередині заголовків. У великих проектах це може створити дуже заплутану ланцюг залежностей, яка запускає більші / довші перебудови, ніж це було потрібно. Перегляньте цю статтю та її подальші дії, щоб дізнатися більше про важливість хорошої фізичної структури в проектах C ++.

Ви повинні включати заголовки всередині заголовка лише тоді, коли це абсолютно необхідно (коли потрібно повне визначення класу), а також використовувати декларацію вперед, де можна (коли потрібен клас - вказівник або посилання).

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


1
як упорядкувати templateдекларацію функції? що має відбуватися в заголовку, ні?
Крістофер

6

Перевірте стандарти кодування Центру космічних польотів Годдарда (для C та C ++). Це виявляється дещо складніше, ніж це було раніше - дивіться оновлені відповіді на запитання SO

Стандарт кодування GSFC C ++ говорить:

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

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


5

Ви праві, що using namespaceв шапці небезпечно. Я не знаю способу, як це скасувати. Виявити його легко, проте просто шукайте using namespaceу файлах заголовків. З цієї останньої причини в реальних проектах це рідкість. Досвідченіші колеги незабаром поскарзяться, якщо хтось зробить щось подібне.

У реальних проектах люди намагаються мінімізувати кількість включених файлів, тому що чим менше ви включаєте, тим швидше вони компілюються. Це економить час усіх. Однак якщо файл заголовка передбачає, що перед ним щось слід включити, він повинен включати його сам. Інакше це робить заголовки недостатніми.


4

Ти правий. І будь-який файл повинен містити лише заголовки, необхідні цьому файлу. Що стосується того, "чи робиш щось не так спільне в реальних проектах?" - о, так!


4

Як і всі речі в програмуванні, прагматизм повинен перемогти догматизм, ІМО.

Поки ви приймаєте рішення в цілому ("Наш проект широко використовує STL, і нам не хочеться передбачати все за допомогою std ::."), Я не бачу в цьому проблеми. Зрештою, ти ризикуєш - це зіткнення з іменами, зрештою, і з повсюдності STL це навряд чи буде проблемою.

З іншого боку, якщо це було рішення одного розробника в одному (не приватному) заголовковому файлі, я бачу, як це призведе до плутанини серед команди і чого слід уникати.


4

Що стосується "Чи є якийсь спосіб скасувати [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декларація знаходиться в області файлу, це поширюватиметься на обсяг будь-якого файлу, до якого входить заголовок.


2
ти, здається, єдиний, як зрозумів актуальне питання ... однак, моя компіляція не дуже рада мені, використовуючи уповільнення класу всередині класу.
іржавий папір

Цю відповідь можна зробити ще кращою, пояснивши проблему з ідеєю ОП про те, як має працювати сфера (як, наприклад, namespaceдекларація) та як вона фактично працює (як змінна). {}додаючи його, обмежують сферу його застосування, {}після того , як вони нічого не стосуються. Це випадковий спосіб, який using namespaceзастосовується у всьому світі.
TafT

2

Я вважаю, що ви можете безпечно використовувати заголовки 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.


Це корисна техніка, яку я раніше не бачив; Дякую. Зазвичай я добре використовую кваліфікацію повного діапазону та вводячи usingдекларації у визначення функцій, де можу, щоб вони не забруднювали простори імен поза функцією. Але тепер я хочу використовувати C ++ 11 визначені користувачем літерали у файлі заголовка, і згідно звичайної конвенції оператори літералу захищені простором імен; але я не хочу використовувати їх у списках ініціалізаторів конструкторів, які не входять до сфери застосування, яку я можу використовувати без забруднення using. Тож це чудово підходить для вирішення цієї проблеми.
Ентоні Хол

Хоча неприємний побічний ефект цієї моделі є те , що будь-які класи , оголошених всередині простору імен таємних будуть відображатися в повідомленнях про помилки компілятора з повним ім'ям: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Принаймні, саме так у мене відбувається в g ++.
Ентоні Хол
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.