GeeksforGeeks
On 16 tammikuun, 2022 by adminKäskyä using namespace std pidetään yleisesti huonona käytäntönä. Vaihtoehto tälle lausekkeelle on määrittää nimiavaruus, johon tunniste kuuluu, käyttämällä scope-operaattoria(::) joka kerta, kun ilmoitamme tyypin.
Vaikka lauseke säästää meidät kirjoittamasta std:: aina kun haluamme käyttää luokkaa tai tyyppiä, joka on määritelty std-nimiavaruudessa, se tuo koko std-nimiavaruuden ohjelman nykyiseen nimiavaruuteen. Otetaanpa muutama esimerkki ymmärtääksemme, miksi tämä ei ehkä ole niin hyvä asia
Esitettäköön, että haluamme käyttää coutia std-nimiavaruudesta. Kirjoitamme siis
Esimerkki 1:
#include <iostream>
using
namespace
std;
cout <<
" Something to Display"
;
Nykyisin myöhäisemmässäkin kehitysasteessa, haluamme käyttää toista versiota coutista, joka on mukautetusti toteutettu jossakin kirjastossa nimeltä ”foo” (esimerkiksi)
#include <foo.h>
#include <iostream>
using
namespace
std;
cout <<
" Something to display"
;
Huomaa, kuinka moniselitteisyys on olemassa, mihin kirjastoon cout osoittaa? Kääntäjä saattaa havaita tämän ja jättää ohjelman kääntämättä. Pahimmassa tapauksessa ohjelma saattaa silti kääntyä, mutta kutsua väärää funktiota, koska emme koskaan määritelleet, mihin nimiavaruuteen tunniste kuuluu.
Nimiavaruudet otettiin käyttöön C++:ssa tunnisteiden nimiristiriitojen ratkaisemiseksi. Näin varmistettiin, että kahdella objektilla voi olla sama nimi ja silti niitä voidaan käsitellä eri tavalla, jos ne kuuluvat eri nimiavaruuksiin. Huomaa, että tässä esimerkissä on tapahtunut juuri päinvastoin. Sen sijaan, että olisimme ratkaisseet nimiristiriidan, luomme itse asiassa nimiristiriidan.
Kun tuomme nimiavaruuden, vedämme kaikki tyyppimäärittelyt nykyiseen laajuuteen. Nimiavaruus std on valtava. Siinä on satoja valmiiksi määriteltyjä tunnuksia, joten on mahdollista, että kehittäjä saattaa jättää huomiotta sen, että std-kirjastossa on toinenkin määritelmä hänen haluamalleen objektille. Tietämättä tästä hän saattaa määritellä oman toteutuksensa ja odottaa, että sitä käytetään ohjelman myöhemmissä osissa. Näin ollen nykyisessä nimiavaruudessa olisi kaksi määritelmää samalle tyypille. Tämä ei ole sallittua C++:ssa, ja vaikka ohjelma kääntyisi, ei voida mitenkään tietää, mitä määritelmää missäkin käytetään.
Ratkaisu ongelmaan on määritellä eksplisiittisesti, mihin nimiavaruuteen tunnisteemme kuuluu käyttämällä scope-operaattoria (::). Näin ollen yksi mahdollinen ratkaisu yllä olevaan esimerkkiin voi olla
#include <foo>
#include <iostream>
std::cout <<
"Something to display"
;
foo::cout <
"Something to display"
;
Mutta joutuu kirjoittamaan std:: joka kerta, kun määrittelemme tyypin, on työlästä. Se myös tekee koodistamme karvaamman näköistä monine tyyppimäärittelyineen ja vaikeuttaa koodin lukemista. Tarkastellaan esimerkiksi koodia, jolla saadaan nykyinen kellonaika ohjelmassa
Esimerkki 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);
Lähdekoodi, joka on täynnä monimutkaisia ja pitkiä tyyppimäärityksiä, ei ole kovin helppolukuista. Tätä kehittäjät pyrkivät välttämään, koska koodin ylläpidettävyys on heille ensisijaisen tärkeää.
On olemassa muutamia tapoja ratkaista tämä dilemma eli määritellä tarkka nimiavaruus roskaamatta koodia std-avainsanoilla.
Harkitse typedefien käyttöä
typedefit säästävät meidät pitkien tyyppimäärittelyjen kirjoittamiselta. Esimerkissämme 1, voisimme ratkaista ongelman käyttämällä kahta typedefsiä yksi std-kirjastolle ja toinen 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"
;
Kokonaisien nimiavaruuksien tuomisen sijaan, tuota typistetty nimiavaruus
Esimerkissä 2 olisimme voineet tuoda vain chrono-nimiavaruuden alle std.
#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);
Voidaan käyttää lauseketta myös yksittäisen tunnisteen tuomiseen. Tuodaksemme vain std::coutin voisimme käyttää
using std::cout;
Jos silti tuot kokonaisia nimiavaruuksia, yritä tehdä se funktioiden sisällä tai rajoitetussa laajuudessa eikä globaalissa laajuudessa.
Käytä ”using namespace std” -lauseketta funktiomäärittelyjen tai luokkien, rakenteiden määritelmien sisällä. Näin nimiavaruusmäärittelyt tuodaan paikalliseen laajuuteen, ja tiedämme ainakin, mistä mahdolliset virheet voivat johtua, jos niitä ilmenee.
#include <isotream>
using
namespace
std;
void
foo()
{
using
namespace
std;
}
Johtopäätös.
Olemme keskustelleet vaihtoehtoisista tavoista käyttää tunnusta nimiavaruudesta. Vältä kaikissa tapauksissa kokonaisten nimiavaruuksien tuomista lähdekoodiin.
Hyvät koodauskäytännöt saattavat viedä aikaa opetteluun ja kehittämiseen, mutta ne kannattavat yleensä pitkällä aikavälillä. Puhtaan, yksiselitteisen ja vankan virheettömän koodin kirjoittamisen pitäisi olla jokaisen ohjelmointikehittäjän tarkoitus.
Vastaa