„Using namespace” w nagłówkach C ++

119

We wszystkich naszych kursach języka c ++ wszyscy nauczyciele zawsze umieszczają w swoich plikach using namespace std;zaraz po #includeznaku s .h. Wydaje mi się to niebezpieczne, ponieważ wtedy dołączając ten nagłówek do innego programu, uzyskam przestrzeń nazw zaimportowaną do mojego programu, być może nie zdając sobie z tego sprawy, nie zamierzając jej lub nie chcąc (dołączanie nagłówka może być bardzo głęboko zagnieżdżone).

Więc moje pytanie jest podwójne: czy mam rację, że using namespacenie powinno się tego używać w plikach nagłówkowych i / lub czy jest jakiś sposób, aby to cofnąć, na przykład:

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

Jeszcze jedno pytanie w tych samych wierszach: czy plik nagłówkowy powinien zawierać #includewszystkie nagłówki, których .cpppotrzebuje odpowiadający mu plik, tylko te, które są potrzebne do definicji nagłówków, i pozostawić resztę .cpppliku #include, czy też nie zadeklarować wszystkiego, czego potrzebuje extern?
Uzasadnienie pytania jest takie samo jak powyżej: nie chcę niespodzianek, gdy włączam.h plików.

Poza tym, jeśli mam rację, czy jest to częsty błąd? Mam na myśli programowanie w świecie rzeczywistym i „rzeczywiste” projekty.

Dziękuję Ci.

Baruch
źródło
3
na marginesie, jeśli wystąpią kolizje nazw z powodu using namespaceinstrukcji, możesz użyć w pełni kwalifikowanej nazwy, aby rozwiązać problem.
Marius Bancila

Odpowiedzi:

115

Zdecydowanie NIE powinieneś używać using namespacew nagłówkach dokładnie z powodu, dla którego mówisz, że może to nieoczekiwanie zmienić znaczenie kodu w innych plikach, które zawierają ten nagłówek. Nie ma sposobu, aby cofnąć, using namespaceco jest kolejnym powodem, dla którego jest tak niebezpieczny. Zwykle używam greplub podobnego, aby upewnić się, że using namespacenie jest to wywoływane w nagłówkach, zamiast próbować czegoś bardziej skomplikowanego. Prawdopodobnie statyczne kontrolery kodu również to zaznaczają.

Nagłówek powinien zawierać tylko te nagłówki, które ma zostać skompilowane. Łatwym sposobem na wymuszenie tego jest zawsze dołączanie własnego nagłówka każdego pliku źródłowego jako pierwszej rzeczy, przed innymi nagłówkami. Wówczas kompilacja pliku źródłowego nie powiedzie się, jeśli nagłówek nie jest samodzielny. W niektórych przypadkach, na przykład w odniesieniu do klas szczegółowych implementacji w bibliotece, możesz użyć deklaracji do przodu zamiast, #includeponieważ masz pełną kontrolę nad definicją takiej zadeklarowanej do przodu klasy.

Nie jestem pewien, czy nazwałbym to powszechnym, ale na pewno pojawia się od czasu do czasu, zwykle napisany przez nowych programistów, którzy nie są świadomi negatywnych konsekwencji. Zwykle tylko niewielka edukacja na temat zagrożeń rozwiązuje wszelkie problemy, ponieważ jest stosunkowo łatwa do naprawienia.

Mark B.
źródło
2
czy możemy swobodnie używać usingwyciągów w naszych .cppplikach? te 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iteratorsą śmiercią dla palców.
Christopher
1
i jak usprawnić templatefunkcje - które mają znajdować się w nagłówkach? typedefs?
Christopher
1
@donlan, wygląda na to, że nie otrzymałeś odpowiedzi od dłuższego czasu ... Tak, możesz używać usinginstrukcji w .cppplikach bez większego problemu, ponieważ zakres będzie ograniczony tylko do tego pliku, ale nigdy nie rób tego przed #includeinstrukcją. Jeśli chodzi o funkcje szablonów zdefiniowane w nagłówkach, niestety nie znam dobrego rozwiązania poza wypisaniem przestrzeni nazw ... Być może mógłbyś umieścić usingdeklarację w oddzielnym zakresie { /* using statement in between brackets */ }, co przynajmniej uniemożliwiłoby jej ucieczkę z bieżącego pliku .
tjwrona1992
26

Pozycja 59 w „Standardach kodowania C ++: 101 reguł, wytycznych i najlepszych praktyk” Suttera i Alexandrescu :

59. Nie pisz użycia przestrzeni nazw w pliku nagłówkowym lub przed #include.

Przestrzenie nazw usingsą dla twojej wygody, a nie dla ciebie, abyś narzucał innym: Nigdy nie pisz usingdeklaracji ani usingdyrektywy przed#include dyrektywą.

Wniosek: w plikach nagłówkowych nie należy zapisywać usingdyrektyw ani usingdeklaracji na poziomie przestrzeni nazw ; zamiast tego jawnie kwalifikuj wszystkie nazwy do przestrzeni nazw.

Plik nagłówkowy jest gościem w co najmniej jednym pliku źródłowym. Plik nagłówkowy zawierającyusing dyrektywy i deklaracje, przenosi również swoich awanturniczych znajomych.

using Deklaracja przynosi jednego kumpla. using Dyrektywa przynosi we wszystkich kumpli w obszarze nazw. Korzystanie zusing namespace std; jest dyrektywą używającą.

Mówiąc poważnie, mamy przestrzenie nazw, aby uniknąć kolizji nazw. Plik nagłówkowy ma zapewnić interfejs. Większość nagłówków jest niezależna od tego, jaki kod może je zawierać, teraz lub w przyszłości. Dodanie usinginstrukcji w nagłówku dla wygody wewnętrznej narzuca te wygodne nazwy wszystkim potencjalnym klientom tego nagłówka. Może to doprowadzić do konfliktu nazw. I to jest po prostu niegrzeczne.

Andy Thomas
źródło
12

Musisz zachować ostrożność, umieszczając nagłówki w nagłówkach. W dużych projektach może stworzyć bardzo splątany łańcuch zależności, który wyzwala większe / dłuższe przebudowy, niż było to faktycznie konieczne. Zapoznaj się z tym artykułem i jego dalszymi działaniami, aby dowiedzieć się więcej o znaczeniu dobrej struktury fizycznej w projektach C ++.

Powinieneś umieszczać nagłówki wewnątrz nagłówka tylko wtedy, gdy jest to absolutnie konieczne (gdy potrzebna jest pełna definicja klasy) i używać deklaracji do przodu, gdziekolwiek możesz (gdy klasa jest wymagana, to wskaźnik lub odwołanie).

Jeśli chodzi o przestrzenie nazw, zwykle używam jawnego zakresu przestrzeni nazw w moich plikach nagłówkowych i umieszczam tylko using namespacew moich plikach cpp.

Mike O'Connor
źródło
1
jak usprawnić templatedeklarację funkcji? to musi się pojawić w nagłówku, nie?
Christopher
6

Sprawdź standardy kodowania Goddard Space Flight Center (dla C i C ++). Okazuje się, że jest to trochę trudniejsze niż kiedyś - zobacz zaktualizowane odpowiedzi na pytania SO:

Standard kodowania GSFC C ++ mówi:

§3.3.7 Każdy plik nagłówkowy powinien zawierać #includepliki potrzebne do kompilacji, zamiast zmuszać użytkowników do korzystania #includez potrzebnych plików. #includesogranicza się do tego, czego potrzebuje nagłówek; inne #includesnależy umieścić w pliku źródłowym.

Pierwsze z pytań odsyłających zawiera teraz cytat ze standardu kodowania GSFC C i uzasadnienie, ale ostatecznie substancja jest taka sama.

Jonathan Leffler
źródło
5

Masz rację, że using namespacenagłówek jest niebezpieczny. Nie wiem, jak to cofnąć. Łatwo go wykryć, ale po prostu wyszukaj using namespacew plikach nagłówkowych. Z tego ostatniego powodu jest to rzadkie w prawdziwych projektach. Bardziej doświadczeni współpracownicy wkrótce będą narzekać, jeśli ktoś zrobi coś takiego.

W prawdziwych projektach ludzie starają się zminimalizować ilość dołączanych plików, ponieważ im mniej włączysz, tym szybciej się kompiluje. To oszczędza czas wszystkim. Jeśli jednak plik nagłówkowy zakłada, że ​​coś powinno być zawarte przed nim, to powinien zawierać to samo. W przeciwnym razie powoduje, że nagłówki nie są samodzielne.

Öö Tiib
źródło
4

Masz rację. Każdy plik powinien zawierać tylko nagłówki wymagane przez ten plik. Jeśli chodzi o „czy robienie rzeczy źle jest powszechne w rzeczywistych projektach?” - o tak!


źródło
4

Jak wszystko w programowaniu, pragmatyzm powinien wygrać z dogmatyzmem, IMO.

Tak długo, jak podejmujesz decyzję dla całego projektu („Nasz projekt intensywnie używa STL i nie chcemy poprzedzać wszystkiego std ::.”), Nie widzę z tym problemu. Jedyne, co ryzykujesz, to w końcu kolizje nazw, a przy wszechobecności STL jest mało prawdopodobne, aby stanowił problem.

Z drugiej strony, gdyby była to decyzja jednego programisty w jednym (nieprywatnym) pliku nagłówkowym, widzę, jak spowodowałaby to zamieszanie w zespole i należy jej unikać.

ijprest
źródło
4

W odniesieniu do „Czy jest jakiś sposób na cofnięcie [a using deklaracji]?”

Myślę, że warto zaznaczyć, że na usingdeklaracje ma wpływ zakres.

#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

Tak skutecznie, tak. Ograniczając zakresusing deklaracji, jej skutek trwa tylko w tym zakresie; jest „cofnięte”, gdy kończy się ten zakres.

Gdy usingdeklaracja jest zadeklarowana w pliku poza jakimkolwiek innym zakresem, ma zasięg pliku i wpływa na wszystko w tym pliku.

W przypadku pliku nagłówkowego, jeśli usingdeklaracja jest w zakresie pliku, będzie to dotyczyło zakresu dowolnego pliku, w którym znajduje się nagłówek.

YoungJohn
źródło
2
wydaje się, że jesteś jedyną osobą, która zrozumiała rzeczywiste pytanie ... jednak moja kompilacja nie jest zbyt zadowolona, ​​że ​​używam wewnątrz klasy spowalniania.
rustypaper
Ta odpowiedź mogłaby być jeszcze lepsza, wyjaśniając problem z koncepcją PO dotyczącą tego, jak zakres powinien działać (jak namespacetreść deklaracji) w porównaniu z tym, jak faktycznie działa (jak zmienna). {}załączenie go ogranicza jego zakres, {}po czym nie robi nic z nim związanego. To jest przypadkowy sposób, że using namespacejest stosowany globalnie.
TafT
2

Uważam, że możesz bezpiecznie używać `` using '' w nagłówkach C ++, jeśli piszesz swoje deklaracje w zagnieżdżonej przestrzeni nazw, takiej jak ta:

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;

Powinno to obejmować tylko rzeczy zadeklarowane w „DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED” bez używanych przestrzeni nazw. Przetestowałem to na kompilatorze mingw64.

AnArrayOfFunctions
źródło
To przydatna technika, której wcześniej nie widziałem; dzięki. Zwykle nie przeszkadzało mi używanie pełnej kwalifikacji zakresu i umieszczanie usingdeklaracji wewnątrz definicji funkcji, gdzie mogę, aby nie zanieczyszczały przestrzeni nazw poza funkcją. Ale teraz chcę użyć literałów zdefiniowanych przez użytkownika C ++ 11 w pliku nagłówkowym i zgodnie ze zwykłą konwencją operatory literału są chronione przez przestrzeń nazw; ale nie chcę ich używać w listach inicjalizujących konstruktory, które nie są w zakresie, w którym mogę użyć usingdeklaracji niezanieczyszczającej . Więc to jest świetne do rozwiązania tego problemu.
Anthony Hall
Chociaż niefortunny efekt uboczny tego wzoru jest to, że wszystkie klasy zadeklarowane wewnątrz przestrzeni nazw najgłębszej pojawią się komunikaty o błędach kompilatora z pełnej nazwy: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Przynajmniej tak się dzieje ze mną w g ++.
Anthony Hall