Zašto se "std imenski prostor" koristi kao loša praksa?

Rečeno mi je da je pisanje koda using namespace std u kodu pogrešno i da umjesto toga trebam koristiti std::cout i std::cin .

Zašto se using namespace std lošom praksom? Je li ona nedjelotvorna ili može deklarirati dvosmislene varijable (varijable koje imaju isto ime kao funkcija u std )? Utječe li na izvedbu?

2212
21 сент. postavio korisnik akbiggs 21. ruj 2009-09-21 06:08 '09 u 6:08 am 2009-09-21 06:08
@ 36 odgovora
  • 1
  • 2

To se uopće ne odnosi na uspješnost. Ali razmotrite ovo: koristite dvije knjižnice pod nazivom Foo i Bar:

 using namespace foo; using namespace bar; 

Sve radi kako treba, možete nazvati Blah() iz Foo i Quux() iz Bara bez ikakvih problema. Ali jednog dana prelazite na novu verziju Foo 2.0, koja sada nudi značajku pod nazivom Quux() . Sada imate konflikt: i Foo 2.0 i Bar import Quux() u vašem globalnom imenskom prostoru. To će zahtijevati određeni napor da se popravi, osobito ako su parametri funkcije isti.

Ako ste koristili foo::Blah() i bar::Quux() , tada bi uvod foo::Quux() bio ne-događaj.

1863
21 сент. dao / la Greg Hewgill 21. rujna 2009-09-21 06:13 '09 u 6:13 2009-09-21 06:13

Slažem se sa svime što je Greg napisao , ali želim dodati: Možda je čak i gore nego što je Greg rekao!

Foo 2.0 knjižnica može uvesti Quux() funkciju, koja je definitivno bolja za neke od vaših Quux() poziva nego bar::Quux() , čiji je kod pozvan mnogo godina. Tada je vaš kod još sastavljen , ali tiho naziva pogrešnu funkciju i Bog zna što. To je otprilike tako loše kao što sve može biti.

Imajte na umu da std sadrži mnoge identifikatore, od kojih su mnogi vrlo česti (think list , sort , string , iterator , itd.), Koji se najvjerojatnije pojavljuju iu drugom kodu.

Ako mislite da je to malo vjerojatno: postavljeno je pitanje o tome kako se to dogodilo (pogrešna funkcija uzrokovana nedostatkom std:: prefiksa). oko pola godine nakon što sam dao ovaj odgovor. Ovdje je još jedan, noviji primjer takvog pitanja. Ovo je pravi problem.


border=0

Ovdje je još jedna točka podataka: prije mnogo godina, također sam se navikla pretpostaviti da ovo nervira potrebu za prefiksom za sve iz standardne std:: library. Tada sam radio u projektu u kojem je na početku odlučeno da su direktive i using deklaracija zabranjeni, s iznimkom područja djelovanja. Pogodi što? Većina nas je imala vrlo malo tjedana da se navikne na pisanje prefiksa, a nakon nekoliko tjedana većina nas se čak složila da je taj kod zapravo učinio čitljivijim. Postoji razlog za to: Ako volite kraću ili dužu prozu, ona je subjektivna, ali prefiksi objektivno dodaju jasnoću kodu. Ne samo kompajler, nego je i lakše vidjeti koji se identifikator spominje.

Za deset godina ovaj je projekt narastao na nekoliko milijuna redaka koda. Budući da se ove rasprave stalno iznova javljaju, jednom sam bio znatiželjan koliko često se u projektu koristi (dopušteno) korištenje funkcije područja. Našao sam izvore za to i pronašao samo jedno ili dvadesetak mjesta na kojima je korišten. Za mene, ovo ukazuje da, jednom pokušali, programeri ne nalaze dovoljno std:: painful da koriste direktive čak i svakih 100 kLoC čak i tamo gdje je dopušteno koristiti.


border=0

Zaključak: eksplicitno prefiks sve ne šteti, vrlo malo se koristi i ima objektivne prednosti. Konkretno, pojednostavljuje tumačenje koda od strane prevodioca i ljudskih čitatelja - i to bi vjerojatno trebalo biti glavni cilj kod pisanja koda.

1214
21 сент. odgovor je dan sbi 21. rujna 2009-09-21 12:26 '09 u 12:26 2009-09-21 12:26

Problem s using namespace u zaglavljenim datotekama vaših klasa je u tome što on prisiljava svakoga tko želi koristiti vaše klase (uključujući datoteke zaglavlja) i na "korištenje" (to jest, vidi sve) u ovim drugim prostorima imena.

Međutim, možete slobodno koristiti izraz upotreba u vašim (privatnim) * .cpp datotekama.


Zapamtite da se neki ljudi ne slažu s mojom izjavom "osjećam se slobodnom" poput ove - jer iako je upotreba izraza cpp bolja od zaglavlja (jer ne utječe na ljude koji uključuju vašu zaglavlje), oni misle da to još uvijek nije dobro (jer, ovisno o kodu, to može zakomplicirati provedbu razreda). Ova tema često postavljanih pitanja govori

Direktiva o korištenju postoji za zastarjeli C ++ kod i olakšava prijelaz na prostor imena, ali ga vjerojatno ne biste trebali koristiti redovito, barem u novom C ++ kodu.

FAQ nudi dvije mogućnosti:

  • Izjava o upotrebi:

     using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:"; 
  • Samo upišite std ::

     std::cout << "Values:"; 
337
21 сент. odgovor od ChrisW 21. rujna 2009-09-21 06:22 '09 u 6:22 am 2009-09-21 06:22

Nedavno sam se suočio s žalbom zbog Visual Studio 2010 . Pokazalo se da gotovo sve izvorne datoteke imaju dvije linije:

 using namespace std; using namespace boost; 

Mnoge Boost funkcije uključene su u C ++ 0x standard, a Visual Studio 2010 ima mnogo C ++ 0x, tako da ti programi nisu kompilirani.

Stoga izbjegavanje using namespace X; Ovo je oblik buduće provjere, način da se osigura da promjene u korištenim knjižnicama i / ili zaglavljenim datotekama ne ometaju program.

213
28 окт. Odgovoriti David Thornley 28. lis 2010-10-28 20:37 '10 u 20:37 2010-10-28 20:37

Kratka verzija: Ne koristite globalnu upotrebu deklaracija ili direktiva u zaglavljenim datotekama. Slobodno ih koristite u svojim implementacijskim datotekama. Ovdje, što Herb Sutter i Andrei Aleksandrescu imaju reći o ovom pitanju u C ++ Standardima kodiranja (podebljano za moju pažnju):

rezime

Imena prostora su za vašu udobnost, a ne za druge: nikada ne zapisujte deklaraciju korištenja ili direktivu o korištenju prije #include direktive.

Posljedica: ne bilježi razinu prostora naziva u zaglavljenim datotekama pomoću direktiva ili pomoću deklaracija; umjesto toga, imenski prostor je eksplicitan - kvalificira sva imena. (Drugo pravilo slijedi od prvog, jer naslovi nikada ne mogu znati da se iza njih mogu pojaviti različiti #includes zaglavlja.)

rasprava

Ukratko: možete i trebate koristiti imenski prostor s deklaracijama i direktivama u vašim implementacijskim datotekama nakon #include i osjećati se dobro. Unatoč ponovljenim tvrdnjama o suprotnom, imenski prostor koji koristi deklaracije i direktive nije zlo i ne teže cilju prostora imena. Umjesto toga, to je ono što vam omogućuje korištenje prostora imena .

178
03 нояб. odgovor dao mattnewport 03 Nov. 2014-11-03 23:00 '14 u 23:00 2014-11-03 23:00

Ne možete koristiti direktivu na globalnoj razini, posebno u naslovima. Međutim, postoje situacije u kojima se to čak primjenjuje u datoteci zaglavlja:

 template <typename FloatType> inline FloatType compute_something(FloatType x) { using namespace std; //no problem since scope is limited return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4)); } 

To je bolje od eksplicitne kvalifikacije ( std::sin , std::cos ...) jer je kraća i ima sposobnost rada s korisnički definiranim tipovima s pomičnim zarezom (preko pretraživanja ovisnog o argumentu).

110
21 сент. odgovor je dan robson.3.14 21 sep . 2009-09-21 18:47 '09 u 18:47 2009-09-21 18:47

Nemojte ga koristiti na globalnoj razini.

Smatra se "lošim" samo kada se koristi globalno . Budući da:

  • Vi zakrčite prostor imena u kojem programirate.
  • Čitatelji će imati poteškoća s otkrivanjem odakle dolazi određeni identifikator kada koristite puno using namespace xyz .
  • Što god vrijedilo za druge čitatelje vašeg izvornog koda, to je još više istinito za najčešće čitatelje: sebe. Vratite se za godinu ili dvije i pogledajte ...
  • Ako govorite samo o using namespace std , možda ne znate sve materijale koje snimite, a kada dodate još #include ili premjestiti na novu verziju C ++-a, možete dobiti konflikte imena za koje niste znali.

Možete ga koristiti lokalno.

Samo naprijed i slobodno ga koristite lokalno (gotovo). To, naravno, sprječava ponavljanje std:: - a ponavljanje je također loše.

Idiom za lokalnu uporabu

U C ++ 03, postojao je idiom - template code - za implementaciju swap funkcije za vaše klase. Predloženo je da zapravo koristite lokalno using namespace std - ili barem using std::swap :

 class Thing { int value_; Child child_; public: // ... friend void swap(Thing  Thing  }; void swap(Thing  Thing  { using namespace std; // make `std::swap` available // swap all members swap(a.value_, b.value_); // `std::stwap(int, int)` swap(a.child_, b.child_); // `swap(Child or `std::swap(...)` } 

Čini sljedeću magiju:

  • value_ će odabrati std::swap za value_ , tj. void std::swap(int, int) .
  • Ako imate implementiran void swap(Child Child> , kompajler će ga odabrati.
  • Ako nemate ovo preopterećenje, kompajler će koristiti void std::swap(Child> i pokušati ih najbolje zamijeniti.

C ++ 11 više nema razloga koristiti ovaj predložak. std::swap implementacija je modificirana kako bi se pronašlo potencijalno preopterećenje i odabrala.

86
18 янв. Odgovor je dao towi 18. siječnja 2013-01-18 12:34 '13 u 12:34 2013-01-18 12:34

Ako uvezete ispravne datoteke zaglavlja, iznenada imate imena kao što su hex , left , plus ili count na globalnoj razini. To može biti iznenađujuće ako ne znate da std:: sadrži ta imena. Ako također pokušavate koristiti ta imena lokalno, to može dovesti do neke zbrke.

Ako su svi standardni elementi u vlastitom imenskom prostoru, ne morate brinuti o sukobu naziva s vašim kôdom ili drugim knjižnicama.

73
21 сент. odgovor je dan sth 21 sep. 2009-09-21 06:23 '09 u 6:23 am 2009-09-21 06:23

Iskusni programeri koriste sve što rješava njihove probleme i izbjegavaju nove probleme koji se pojave, te izbjegavaju korištenje direk- tora pokazivača na razini zaglavlja iz tog razloga.

Iskusni programeri također pokušavaju izbjeći potpuno kvalificiranje imena unutar svojih izvornih datoteka. Manji razlog za to je da nije elegantno pisati više koda ako nema dovoljno koda, ako nema uvjerljivih razloga. Glavni razlog za to je da je argument ovisan o pretraživanju (ADL) onemogućen.

Koji su to dobri razlozi? Ponekad programeri očito žele onemogućiti ADL, u drugim slučajevima žele eliminirati nejasnoće.

Dakle, sve je u redu:

  • Korištenje direktiva na razini funkcija i korištenje deklaracija u okviru provedbe funkcija
  • Koristite izjave na izvornoj razini u izvornim datotekama
  • (Ponekad) pomoću razine-na razini izvorne datoteke
39
29 марта '11 в 11:10 2011-03-29 11:10 odgovor je dao Alexander Poluektov 29. ožujka '11 u 11:10 2011-03-29 11:10

Drugi razlog je iznenađenje.

Ako vidim cout << blah umjesto std::cout << blah

Mislim što je ovo? Je li to normalan cout ? Je li to nešto posebno?

37
21 сент. Odgovor dao Martin Beckett 21. rujna 2009-09-21 06:13 '09 u 6:13 2009-09-21 06:13

Slažem se da se ne može koristiti globalno, ali ne toliko zlo da se koristi lokalno, kao u namespace . Ovdje je primjer iz "C ++ programskog jezika":

 namespace My_lib { using namespace His_lib; // everything from His_lib using namespace Her_lib; // everything from Her_lib using His_lib::String; // resolve potential clash in favor of His_lib using Her_lib::Vector; // resolve potential clash in favor of Her_lib } 

U ovom primjeru riješili smo potencijalne kolizije imena i nejasnoće povezane s njihovim sastavom.

Imena koja su tamo eksplicitno deklarirana (uključujući imena deklarirana kao deklaracije kao što je His_lib::String ) imaju prednost nad imenima koja su dostupna u drugom području koristeći using namespace Her_lib ( using namespace Her_lib ).

36
29 авг. Odgovor je dan Oleksiy 29 aug. 2013-08-29 12:44 '13 u 12:44 2013-08-29 12:44

Također smatram da je to loša praksa. Zašto? Samo sam jednom pomislio da je funkcija prostora za imenovanje podjela materijala kako ga ne bih pokvarila bacajući sve u jednu globalnu torbu. Međutim, ako često koristim "cout" i "cin", pišem: using std::cout; using std::cin; using std::cout; using std::cin; u cpp datoteci (nikad u datoteci zaglavlja, budući da se distribuira pomoću #include ). Mislim da nitko pametan nikada neće nazvati stream cout ili cin .;)

26
21 сент. odgovor je dan Yelonek 21 sep . 2009-09-21 12:34 '09 u 12:34 2009-09-21 12:34

Lijepo je vidjeti kod i znati što on radi. Ako vidim std::cout , znam da je cout stream std knjižnice. Ako vidim cout , onda ne znam. To može biti tok iz std knjižnice. Ili možda int cout = 0; deset linija više u istoj funkciji. Ili static varijabla cout u ovoj datoteci. To može biti bilo što.

Sada uzmite milijun linija linijskog koda koji nije posebno velik, i tražite pogrešku, što znači da znate da postoji jedan redak u ovom milijunskom retku koji ne čini ono što bi trebalo biti učinjeno. cout << 1; može čitati static int nazvan cout , pomaknuti ga ulijevo i odbaciti rezultat. Tražim pogrešku, moram to provjeriti. Vidite li kako stvarno volim vidjeti std::cout ?

To je jedna od tih stvari koja izgleda kao stvarno dobra ideja ako ste učitelj i nikada niste morali pisati i održavati bilo koji kod za život. Volim vidjeti kod gdje (1) znam što radi; i, (2) siguran sam da je osoba koja je napisala ovo znala što radi.

21
13 марта '14 в 20:22 2014-03-13 20:22 odgovor je dan gnasher729 13. ožujka '14 u 20:22 2014-03-13 20:22

Sve o upravljanju složenosti. Korištenje prostora imena će dovesti do nečega što ne želite, i stoga je možda teže debagirati (možda kažem). Korištenje std :: svugdje je teže za čitanje (više teksta i sve to).

Konji za tečajeve - upravljati svojom složenošću možete i osjećati.

20
21 сент. odgovor je dan Preet Sangha 21 sep . 2009-09-21 06:14 '09 u 6:14 am 2009-09-21 06:14

Čini se da je upotreba mnogih prostora za imenovanje u isto vrijeme recept za katastrofu, ali upotreba JUST prostora naziva std i samo std imenski prostor nije velika stvar, po mom mišljenju, jer se redefiniranje može dogoditi samo vašim vlastitim kodom .. ,

Zato ih smatrajte rezerviranim imenima, kao što su "int" ili "class", i to je sve.

Ljudi bi trebali prestati biti tako analni. Vaš učitelj je bio u pravu cijelo vrijeme. Samo koristite JEDAN imenski prostor; to je cijela svrha korištenja prostora imena na prvom mjestu. Ne smijete koristiti više od jednog po jednog. Ako ovo nije tvoje. Dakle, redefinicija se neće dogoditi.

17
09 нояб. odgovor dao user2645752 09 Nov. 2013-11-09 18:09 '13 u 18:09 2013-11-09 18:09
  • Morate moći čitati kodove koje su napisali ljudi koji imaju različita mišljenja o stilu i najboljoj praksi od vas.

  • Ako koristite cout, nitko nije zbunjen. Ali kada imate puno prostora za imenovanje koji letite, i vidite ovu klasu, a niste sasvim sigurni što radi, jer eksplicitni imenski prostor djeluje kao neka vrsta komentara. Na prvi pogled možete vidjeti: "Oh, ovo je rad s datotečnim sustavom" ili "Što čini mrežne stvari."

17
21 сент. odgovor je dao Dustin Getz 21 sep. 2009-09-21 07:04 '09 u 7:04 2009-09-21 07:04

Razmotrit će

 // myHeader.h #include <sstream> using namespace std; // someoneElses.cpp/h #include "myHeader.h" class stringstream { // uh oh }; 

Imajte na umu da je ovo jednostavan primjer, ako imate datoteke s uključenim 20 i drugim uvozom, imat ćete mnogo ovisnosti da riješite problem. Najgore od svega, možete dobiti nepovezane pogreške u drugim modulima, ovisno o definicijama koje se sukobljavaju.

To nije strašno, ali ćete se riješiti glavobolja ako ga ne koristite u zaglavljenim datotekama ili u globalnom prostoru imena. Najvjerojatnije, to se može učiniti na vrlo ograničenim područjima, ali nikada nisam imao problema s unosom dodatnih 5 znakova kako bih otkrio odakle dolaze moje funkcije.

16
21 сент. odgovor je dao Ron Warholic 21 sep . 2009-09-21 06:19 '09 u 6:19 AM 2009-09-21 06:19

Poseban primjer za razjašnjenje problema. Zamislite da imate situaciju u kojoj imate 2 knjižnice, foo i bar, svaki s vlastitim imenskim prostorom:

 namespace foo { void a(float) {  } } namespace bar { ... } 

Recimo da koristite vlastiti program i traku zajedno u svom programu na sljedeći način:

 using namespace foo; using namespace bar; void main() { a(42); } 

U ovom trenutku sve je u redu. Kada pokrenete svoj program, on "nešto radi". No kasnije ćete ažurirati ploču i reći da se ona promijenila ovako:

 namespace bar { void a(float) {  } } 

U ovom trenutku dobit ćete pogrešku kompilatora:

 using namespace foo; using namespace bar; void main() { a(42); // error: call to 'a' is ambiguous, should be foo::a(42) } 

Dakle, trebat ćete učiniti nešto za održavanje da biste pojasnili što znači "a" (tj. foo::a ). To je vjerojatno nepoželjno, ali, na sreću, ovo je prilično jednostavno (samo dodajte foo:: prije svih poziva koje prevodilac označava kao dvosmislene).

Ali zamislite alternativni scenarij u kojem se traka umjesto toga promijenila kako bi izgledala ovako:

 namespace bar { void a(int) {  } } 

U ovom trenutku, vaš poziv na a(42) odjednom postaje vezan za bar::a umjesto foo::a i umjesto da nešto radi, on radi nešto sasvim drugo. Nema upozorenja kompilatora ili bilo čega drugog. Vaš program upravo počinje raditi nešto sasvim drugačije nego prije.

Kada koristite prostor imena, riskirate sličan scenarij, tako da su ljudi neugodno koristiti prostor. Što je više stvari u imenskom prostoru, to je veći rizik od sukoba, tako da ljudi mogu biti još nepogodniji za korištenje std imenskog prostora (zbog broja stvari u ovom imenskom prostoru) od ostalih prostora.

U konačnici, ovo je kompromis između mogućnosti snimanja i pouzdanosti / održivosti. Čitljivost može također biti važna, ali svejedno mogu vidjeti argumente za to. Normalno, rekao bih da su pouzdanost i održivost važniji, ali u ovom slučaju ćete stalno plaćati troškove snimanja za prilično rijetku pouzdanost / mogućnost održavanja. “Najbolji” kompromis će odrediti vaš projekt i vaše prioritete.

11
02 сент. odgovor od Kevina 02 ruj 2016-09-02 23:06 '16 u 23:06 2016-09-02 23:06

Prostor imena je imenovani opseg. Prostori imena koriste se za grupiranje povezanih deklaracija i odvojene stavke. Например, две отдельно разработанные библиотеки могут использовать одно и то же имя для обозначения разных но пользователь может использовать оба:

 namespace Mylib{ template<class T> class Stack{  }; / / ... } namespace Yourlib{ class Stack{  }; / / ... } void f(int max) { Mylib: :Stack<int> s1(max) ; / / use my stack Yourlib: :Stack s2(max) ; / / use your stack / / ... } 

Повторение имени пространства имен может быть отвлечением как для читателей, так и для писателей. Следовательно, возможно чтобы указать, что имена из определенного пространства имен доступны без явной квалификации. Na primjer:

 void f(int max) { using namespace Mylib; / / make names from Mylib accessible Stack<int> s1(max) ; / / use my stack Yourlib: :Stack s2(max) ; / / use your stack / / ... }