Co to jest zanieczyszczenie „używaniem przestrzeni nazw”?

15

Patrzyłem na przewodnik kodowania google [tutaj] i nie zaleca się używania using namespacelub namespace::function- jeśli nie źle go zinterpretowałem.

Czy dotyczy to stdrównież? cout<<bez niego nie działa. Ta książka poleca to samo. Jak więc korzystać z cout<<bez using namespace std;lub std::cout<<?

Jaki jest zalecany sposób? std::cout<<? Większość podręczników c ++ uczy początkujących, using namespace std;czy propagują słabą praktykę kodowania?

Lord Loh.
źródło

Odpowiedzi:

18

Jak czytam standard Google, nie można using namespace foo;nigdzie używać dyrektywy. Dyrektywa wprowadza wszystko zadeklarowane w przestrzeni nazw i jest częstą przyczyną kolizji i nieoczekiwanego zachowania. Inni cytowali bardzo powszechny: masz gdzieś własną metodę max lub min, która zderza się z plikiem src, w którym ktoś zawiera nagłówek twojej metody, a następnie mówiusing namespace std;

W niektórych miejscach dozwolone jest posiadanie deklaracji użytkowania, która ma formę using ::foo::bar;

Ludzie lubią umieszczać w swoich kodach dyrektywy, ponieważ oszczędzają dużo pisania, ale wiążą się z ryzykiem. Jeśli masz plik z wieloma instrukcjami cout, rozumiem, że nie chcę pisać std :: cout sto razy, ale możesz po prostu powiedzieć, używając :: std :: cout. Traktuję je jak deklaracje zmienne: zakreśl je tam, gdzie są potrzebne. Jeśli jedna funkcja w pliku 10 musi zapisać dane wyjściowe, nie deklaruj metody cout u góry, umieść ją w tej funkcji, która wykonuje dane wyjściowe.

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

To trochę ekstremalne, gdy tylko kilka wierszy generuje dane wyjściowe, ale masz pomysł.

Inną rzeczą, którą można zrobić, jest alias lub typedef, aby zminimalizować pisanie. Nie uważam, aby std :: cokolwiek było tak źle, ale mamy ogromny zestaw źródeł z kilkadziesiąt modułów i czasami musimy pisać kod jak console_gui::command_window::append("text"). To po pewnym czasie staje się nudne i powoduje wiele długich linii. Jestem za czymś takim

typedef console_gui::command_window cw;
cw::append("text");

tak długo, jak aliasy są wykonywane w zasięgu lokalnym i zachowują wystarczający kontekst, aby kod był czytelny.

Michael Mathews
źródło
1
Dzięki! To jest naprawdę pomocne. Nie tylko wyjaśniłeś, dlaczego jest źle z ładnymi przykładami, ale także wskazałeś rozwiązania z ładnymi przykładami. :-)
Lord Loh.
1
BTW: Używanie std::endldo jawnego flushowania na stdout/ stderrjest zwykle dość zbędne, te strumienie są powiązane z stdout/ i stderrtak. To nawet trochę spowalnia.
Deduplicator
8

Jest tak, ponieważ: 1) pokonuje cały cel przestrzeni nazw, którym jest ograniczenie kolizji nazw; 2) udostępnia globalnej przestrzeni nazw całą przestrzeń nazw określoną w dyrektywie using.

Na przykład, jeśli włączysz i zdefiniujesz własną funkcję max (), zderzy się ona ze std :: max ().

http://en.cppreference.com/w/cpp/algorithm/max

Preferowane jest użycie std :: member_you_wish_to_use, ponieważ wyraźnie określa, której przestrzeni nazw użyć.

Szarlotka
źródło
Zakładam, że oznacza to, że powinienem używać std::max()z prefiksem przestrzeni nazw. Czy się mylę?
Lord Loh.
3
Oznacza to, że jeśli wstawisz „using namespace std;” w kodzie pojawią się błędy, jeśli zdefiniujesz własną maksymalną funkcję (lub dowolną inną nazwę już zdefiniowaną w przestrzeni nazw std)
Chewy Gumball
1
Oznacza to po prostu, że powinieneś być ostrożny z usingdyrektywami, ponieważ w tym przypadku zepsułoby to funkcję max (), gdybyś ją zdefiniował i włączył <algorytm>. To prosty przypadek, ale nigdy nie wiesz, co możesz złamać. Musisz znać całą bibliotekę, aby mieć pewność, że jej nie złamałeś, ale nie wiesz, czy Twój kod się zepsuje (tj. Kolizja nazw) w przyszłości.
ApplePie
6

Cytując podany przez Ciebie link:

Możesz użyć deklaracji użycia w dowolnym miejscu pliku .cc oraz w funkcjach, metodach lub klasach w plikach .h.

// OK w plikach .cc.

// Musi znajdować się w funkcji, metodzie lub klasie w plikach .h.

using :: foo :: bar;

Styl Google zabrania używania importowania przestrzeni nazw w kontekście globalnym, ale pozwala to robić w lokalnych.

Wszędzie, gdzie użycie deklaracji wpływa tylko na ograniczoną i wyraźnie widoczną część kodu, jest to całkowicie akceptowalne.

Gdy zanieczyszczasz kontekst globalny, wpływa to na niepowiązany kod (domyślnie za pomocą nagłówka). Nic się nie dzieje, gdy robisz to w kontekście lokalnym.

Basilevs
źródło
Mamy te same standardy. Niektórzy z naszych ludzi będą lokalnie pisać w długiej przestrzeni nazw. np. typedef foolicious :: barlicious fb; fb :: drink d;
Michael Mathews
1

nie zaleca się korzystania z przestrzeni nazw lub przestrzeni nazw: funkcja` - jeśli nie źle ją zinterpretowałem.

Zrobiłeś. To zalecenie dotyczy tylko using namespacedyrektywy (która jest powszechnie nazywana abusing namespace, nie do końca dowcipnie). Zdecydowanie zaleca się stosowanie w pełni kwalifikowanej nazwy funkcji lub obiektu, na przykład std::cout.

H2CO3
źródło
1

Chociaż pytanie ma już przydatne odpowiedzi, jeden szczegół wydaje się zbyt krótki.

Większość programistów jest na początku trochę mylona ze usingsłowem kluczowym i opisami namespaceużycia, nawet jeśli próbują się tego nauczyć, przeglądając odniesienie, ponieważ deklaracja i dyrektywa brzmią w pewnym stopniu równoważnie, oba są stosunkowo abstrakcyjnymi długimi słowami zaczynającymi się na d .

Identyfikatory w przestrzeniach nazw są dostępne poprzez jawne nazwanie przestrzeni nazw:

myNamespace::myIdent

może to być o wiele więcej kluczy do pisania. Ale może również zmniejszyć znaczenie twojego kodu, jeśli większość identyfikatorów zostanie prefiksowanych w ten sam sposób. Słowo usingkluczowe pomaga zapobiegać tym negatywnym stronom przestrzeni nazw. Odusing działa na poziomie kompilatora (nie ma makra), jego efekt trwa przez cały zakres, w którym jest używany. Dlatego styl Google ogranicza jego użycie do dobrze zdefiniowanych zakresów, tj. Klas w plikach nagłówkowych lub funkcji w plikach CPP.

... oczywiście istnieje różnica między używaniem deklaracji

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

i stosując dyrektywę

using myNamespace; // make all identifiers of myNamespace directly accessible

Jeśli jest stosowany w ogromnych zakresach, ten ostatni prowadzi do znacznie większego zamieszania.

Wilk
źródło
1

Proszę bardzo:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

Pisząc to w ten sposób, unikamy podatnych na błędy ADL wraz z używaniem dyrektyw i deklaracji.

To ma być sarkastyczna odpowiedź. :-RE

W tej sprawie jestem z Herbem Sutterem nad Google. Z standardów kodowania C ++:

Możesz i powinieneś używać przestrzeni nazw, używając deklaracji i dyrektyw w plikach implementacyjnych po #include dyrektywach i dobrze się z tym czujesz. Pomimo powtarzających się twierdzeń przeciwnych, przestrzeń nazw z deklaracjami i dyrektywami nie jest zła i nie pokonuje celu przestrzeni nazw. Są raczej tym, co sprawia, że ​​przestrzenie nazw są użyteczne .

Możesz mieć obsesję na punkcie potencjalnych konfliktów przestrzeni nazw, które prawdopodobnie nigdy się nie pojawią i prawdopodobnie nie będą trudne do naprawienia w tak rzadkim astronomicznie wydarzeniu, ostrożnie unikając usingdyrektyw i wyraźnie określając każdą rzecz, której używasz (w zależności od operatora) wraz z usingdeklaracjami lub po prostu idź i zacznij using namespace std. Polecam to drugie z punktu widzenia produktywności.

Większość podręczników c ++ uczy początkujących posługiwania się przestrzenią nazw std; czy propagują słabą praktykę kodowania?

Przeciwnie, jeśli mnie zapytasz, i uważam, że Sutter powyżej się zgadza.

Teraz w trakcie mojej kariery napotkałem łącznie około 3 konfliktów przestrzeni nazw, będących bezpośrednim wynikiem usingdyrektyw w bazach kodów obejmujących dziesiątki milionów LOC. Jednak we wszystkich 3 przypadkach znajdowały się one w plikach źródłowych, które obejmowały ponad 50 000 wierszy starszego kodu, pierwotnie napisane w C, a następnie zamaskowane na C ++, wykonując ogromną eklektyczną listę różnych funkcji, w tym nagłówki z kilkunastu różnych bibliotek i posiadające epicka lista#includes obejmująca całą stronę. Pomimo epickiego bałaganu nie były zbyt trudne do naprawienia, ponieważ powodowały błędy kompilacji w OSX (tym, w którym nie udało się zbudować kodu), a nie błędy w czasie wykonywania. Nie organizuj swojego kodu w ten koszmarny sposób i wszystko powinno być w porządku.

To powiedziawszy, unikaj zarówno using dyrektyw, jak i deklaracji w plikach nagłówkowych. To po prostu opóźnione. Ale w przypadku plików źródłowych, a zwłaszcza tych, które nie mają całej strony wypełnionej #includedyrektywami, powiedziałbym, że nie przejmuj się, jeśli nie pracujesz dla Google.


źródło