GeeksforGeeks
On január 16, 2022 by adminA namespace std használata általánosan rossz gyakorlatnak számít. Ennek a kijelentésnek az alternatívája az, hogy minden egyes típus deklarálásakor a hatókör-operátor(::) segítségével megadjuk azt a névteret, amelyhez az azonosító tartozik.
Az utasítás ugyan megkímél bennünket az std:: beírásától, amikor az std névtérben definiált osztályhoz vagy típushoz kívánunk hozzáférni, de az std névtér egészét importálja a program aktuális névterébe. Nézzünk néhány példát, hogy megértsük, miért nem biztos, hogy ez olyan jó dolog
Tegyük fel, hogy a cout-ot az std névtérből szeretnénk használni. Tehát azt írjuk
Példa 1:
#include <iostream>
using
namespace
std;
cout <<
" Something to Display"
;
Most a fejlődés későbbi szakaszában, a cout egy másik változatát szeretnénk használni, amelyet egy “foo” nevű könyvtárban (például)
#include <foo.h>
#include <iostream>
using
namespace
std;
cout <<
" Something to display"
;
Figyeljük meg, hogy van egy kétértelműség, Melyik könyvtárra mutat a cout? A fordító ezt észreveheti, és nem fordítja le a programot. A legrosszabb esetben a program mégis lefordítható, de rossz függvényt hív meg, mivel nem adtuk meg, hogy az azonosító melyik névtérbe tartozik.
A névtereket azért vezették be a C++-ban, hogy feloldják az azonosítónév-konfliktusokat. Ez biztosította, hogy két objektumnak lehet ugyanaz a neve, és mégis különbözőképpen kezelhetők, ha különböző névterekhez tartoznak. Vegyük észre, hogy ebben a példában pont az ellenkezője történt. Ahelyett, hogy feloldanánk egy névkonfliktust, valójában egy névkonfliktust hozunk létre.
Amikor importálunk egy névteret, lényegében az összes típusdefiníciót behúzzuk az aktuális hatókörbe. Az std névtér hatalmas. Több száz előre definiált azonosító van benne, így lehetséges, hogy egy fejlesztő figyelmen kívül hagyja, hogy a tervezett objektumának van egy másik definíciója is az std könyvtárban. Ennek nem tudatában a saját implementációjukat adhatják meg, és elvárhatják, hogy azt a program későbbi részeiben használják. Így ugyanannak a típusnak két definíciója létezne az aktuális névtérben. Ez a C++-ban nem megengedett, és még ha a program le is fordítja, nem lehet tudni, hogy melyik definíciót hol használják.
A probléma megoldása az, hogy a scope operátor (::) segítségével explicit módon megadjuk, hogy az azonosítónk melyik névtérbe tartozik. Így a fenti példa egyik lehetséges megoldása lehet
#include <foo>
#include <iostream>
std::cout <<
"Something to display"
;
foo::cout <
"Something to display"
;
De mivel be kell írni std:: minden egyes alkalommal, amikor definiálunk egy típust, fárasztó. A kódunkat is szőrösebbé teszi a sok típusdefinícióval, és megnehezíti a kód olvasását. Vegyük például az aktuális idő lekérdezésének kódját a programban
Példa 2:
.
#include <chrono>
#include <iostream>
auto
start = std::chrono::high_performance_clock::now()
auto
stop
= std::chrono::high_peformance_clock::now();
auto
duration
= std::duration_cast<std::chrono::milliseconds>(stop - start);
A bonyolult és hosszú típusdefiníciókkal telezsúfolt forráskód nem túl könnyen olvasható. Ezt a fejlesztők igyekeznek elkerülni, mivel a kód karbantarthatósága elsősorban fontos számukra.
Ez a dilemma megoldására van néhány módszer, azaz pontos névtér megadása anélkül, hogy a kódot std kulcsszavakkal szemetelnénk.
Gondoljunk a tipedefek használatára
A tipedefek megmentenek minket a hosszú típusdefiníciók írásától. A mi példánkban 1, megoldhatnánk a problémát két typedef használatával, egy az std könyvtárhoz és egy másik a foo
#include <foo>
#include <iostream>
typedef
std::cout cout_std;
typedef
foo::cout cout_foo;
cout_std <<
"Something to write"
;
cout_foo <<
"Something to write"
;
A teljes névterek importálása helyett, egy csonka névtér importálása
A 2. példában csak a chrono névteret importálhattuk volna az std alatt.
#include <chrono>
#include <iostream>
using
std::chrono;
auto
start = high_performance_clock::now();
auto
stop = high_performance_clock::now();
auto
duration duration_cast<milliseconds>(stop - start);
Az utasítást használhatjuk egyetlen azonosító importálására is. Ha csak az std::cout-ot szeretnénk importálni, akkor használhatjuk a
using std::cout;
Ha mégis egész névtereket importálunk, akkor próbáljuk ezt függvényeken belül vagy korlátozott hatókörben tenni, és ne globális hatókörben.
A “using namespace std” utasítást függvénydefiníciókban vagy osztály, strukt definíciókban használjuk. Ezzel a névtérdefiníciókat helyi hatókörbe importáljuk, és legalább tudjuk, honnan származhatnak az esetleges hibák, ha mégis felmerülnek.
#include <isotream>
using
namespace
std;
void
foo()
{
using
namespace
std;
}
Következtetés.
A névtérből egy azonosító elérésének alternatív módszereit tárgyaltuk. Minden esetben kerüljük a teljes névterek importálását a forráskódba.
A jó kódolási gyakorlatok megtanulása és fejlesztése ugyan időbe telik, de hosszú távon általában kifizetődő. A tiszta, egyértelmű és robusztus, hibamentes kód írása minden programozó fejlesztő célja kell, hogy legyen.
Vélemény, hozzászólás?