Korzystanie z przestrzeni nazw w C++

Dziś chciałbym pomarudzić o tym jak to genialni programiści olewają i ignorują istnienie tak genialnego wynalazku w C++ jak przestrzenie nazw.

Przestrzenie nazw rozwiązują problem kolizji nazw, szczególnie znany osobom programującym w C, gdzie istnieje jedna globalna przestrzeń nazw dla wszystkich nazw. A tym samym utrudnia pisanie programów, i odrębnych modułów.

Do rozwiązania problemu przestrzeni nazw można użyć wielu różnych sposobów i mechanizmów. Jednym z nich jest stosowanie rożnych wymyślnych prefiksów i sufiksów do definiowanych nazw zmiennych, czy typów, a tym samym powstają długie i kłopotliwe nazwy będące potworkami i zlepkiem kilku słów.

Na szczęście w C++ możemy zapomnieć o tych potworkach i wyrażać się jasno, krotko i zwięźle, a wszystko poprzez zdefiniowanie zasięgu, w którym dane nazwy będą obowiązywać, czyli przestrzeni nazw. Wprowadzono je do języka C++ w wyniku głosowania na posiedzeniu w Monachium w lipcu 1993 roku.

Korzystanie z nazw zawartych w przestrzeniach nazw jest możliwe na 3 sposoby.

Standardowo poprzez jawne kwalifikowanie ich użycia, czyli podanie pełnego identyfikatora przestrzeni nazw:

std::string name = "malcom";

Skorzystanie z deklaracji używania, która stworzy synonim dla nazwy w bieżącym zasięgu lokalnym, aby można było korzystać bez kwalifikowania nazwy danej przestrzeni nazw:

using std::string;
string name = "malcom";

Przez dyrektywę używania udostępniając w bieżącym zasięgu wszystkie nazwy z danej przestrzeni nazw:

using namespace std;
string name = "malcom";

I tutaj dochodzimy do sedna notatki. W wielu przypadkach programiści korzystają z ostatniej metody, w większości używane do włączenia przestrzeni nazw standardowej biblioteki, co gorsza dyrektywę tą „wrzucają” odruchowo na samym początku pliku, tuż po włączeniu nagłówków. Często tłumaczą się tym, że „tak będzie lepiej, prościej”, że „nie będę musiał pisać std:: za każdym razem”, itd.

Może wydaje się to sensowne, ale jest beznadziejnym krokiem, za który powinno się zakazywać takim ludziom wykonywanie swojego zawodu! O mój boże, niektórzy idą jeszcze dalej i „robią to” także w plikach nagłówkowych, samobójcy, dla nich już nie ma ratunku.

Nie po to wprowadzono przestrzenie nazw, aby zaraz je „wyłączać” za pomocą dyrektywy używania i z powrotem włączać wszystkie nazwy do globalnej przestrzeni nazw. Oczywiście zabieg taki czasem się przydaje w pojedynczych jednostkach translacji (plikach cpp), szczególnie przy różnego rodzajach testach czy przykładach, ale w produkcyjnym kodzie jest to niestosowne, jest błędem, za który możemy słono zapłacić.

Najgorsze jest użycie takiej dyrektywy w nagłówku, wystarczy choć w jednym umieścić taką linijkę, a skutki będą oddziaływać na cały projekt, nazwy będą widoczne w globalnej przestrzeni w każdym miejscu programu. O ile to Twój program, i nie masz zamiaru rozpowszechniać kodu do dalszego użycia, problem pozostaje tylko Twój, i nic mi do tego. Ale gdy tworzysz oprogramowanie, które będzie wykorzystywane przez innych deweloperów, popełniłeś niewybaczalny błąd. Dyrektywa ta będzie oddziaływać także na kod programisty korzystającego z Twojego kodu, zaśmiecając jego globalną przestrzeń nazw i wprowadzając wiele problemów, nieraz tak ukrytych, że trudno znaleźć przyczynę. Nikt Ci tego nie wybaczy, w najlepszym przypadku „oleją” Twój kod i skorzystają z konkurencji, i zapewne nigdy nie spróbują skorzystać z jakiegokolwiek twojego kolejnego genialnego kodu w swoich kolejnych projektach.

Myślałem, ze wykorzystanie dyrektywy używania mogę spotkać jedynie w różnych kodach początkujących adeptów sztuki programowania, że takie błędy użycia tego mechanizmu w nagłówkach raczej jest nie możliwe. A jednak myliłem się, ostatnio natrafiłem przypadkiem na kod jakiegoś programisty, który szczycił się dobrą znajomością C++ z takim bublem w kodzie.

Tak, więc pozostaje odpowiedzieć na pytanie jak dobrze korzystać z przestrzeni nazw.

Bardzo prosto, używać zawsze pierwszego sposobu, czyli jawnie kwalifikować nazwę przestrzeni nazw. Jeśli, z jakiś powodów jest to kłopotliwe, bądź przestrzenie są kilkukrotnie zagnieżdżone, możemy wykorzystać deklaracje użycia tylko do tych nazw, które rzeczywiście będą nam potrzebne, a w pewnych sytuacjach także dyrektywę użycia. Z jednym małym wyjątkiem, używać using w jak najmniejszym zasięgu jaki tego wymaga. Przestrzenie nazw podlegają regułom zasięgu i widoczności, zatem „odkrywanie” przestrzeni powinno być podobne jak najmniejsze, podobnie jak zasięg zmiennych.

Czyli jeśli mamy funkcję, która wewnętrznie usilnie korzysta z std::string, najłatwiej jest skorzystać z deklaracji using w ciele funkcji:

void MyFunction() {
	using std::string;
 
	string str = "malcom";
	string::size_pos pos = str.find(...);
 
	...
}

Pisząc krotko, unikać jak ognia dyrektywy używania, a stosować jawne kwalifikowanie nazw i ewentualnie deklarację używania w celu łatwego i wygodnego posługiwania się nazwami z innych przestrzeni nazw.

Bjarne Stroustrup ma podobne zadnie na ten temat:

Osobiście uważam dyrektywy używania przede wszystkim za narzędzie służące do przechodzenia do korzystania z przestrzeni nazw. Można bardziej przejrzyście napisać większość programów, jeżeli korzysta się z jawnego kwalifikowania nazw i deklaracji używania tam, gdzie odnosimy się do nazw z innych przestrzeni nazw.

Notatka ta nie wyczerpuje całego tematu używania i korzystania z przestrzeni nazw, sygnalizuje jedynie problem bardzo zlej praktyki związanej z wykorzystaniem dyrektywy używania, nie tylko przez początkujących. Co w wielu przypadkach staje się złym nawykiem, który później jest źródłem wielu bledów nie tylko w naszym kodzie.

Przestrzenie nazw są wspaniałym narzędziem, i powinniśmy z niego korzystać, a także organizować nasz kod i jego strukturę z ich użyciem. I to nie tylko w sytuacji kiedy piszemy bibliotekę, czy moduł, ale także w wielu nieco większych programach pozwalają na lepszą organizację kodu oraz co ważne na używanie krótkich i zwięzłych nazw, niejednokrotnie tych samych w różnych miejscach, bez strachu przed kolizją nazw lub niejednoznacznością.

Przestrzenie nazw wydają się bardzo prostym mechanizmem i w rzeczywistości takim są, ale posiadają dosyć duże możliwości niż się może wydawać na pierwszy rzut oka. Kiedyś wspominałem o aliasowaniu przestrzeni nazw – mechanizmie, o którym niektórzy być może nie słyszeli, a zajmują się językiem C++ od dawna. Innym ciekawym narzędziem są anonimowe przestrzenie nazw które nieco różnią się od zwykłych nazwanych, mam nadzieję, że będę miał okazję za niedługo o tym trochę pomówić. Deklaracja używania pełni również jakieś funkcje w klasach i dziedziczeniu oraz wzorach.

4 przemyślenia nt. „Korzystanie z przestrzeni nazw w C++”

  1. Myślałem, ze wykorzystanie dyrektywy używania mogę spotkać jedynie w różnych kodach początkujących adeptów sztuki programowania

    To mnie rozbawilo, chyba krotko zyjesz na tym swiecie i niewiele jeszcze widziales.
    Ja jak zaczalem prace w firmie produkujacej ceniony w Europie system przezylem niezly szok. Kod, ktorego bym sie nie odwazyl nigdy napisac w moich niekomercyjnych programach stanowi chyba z 85% calego kodu :). Zlamane sa tam doslownie wszystkie zasady projektowania i programowania. W nowszych projektach jest juz lepiej ale projekty rozwijane od lat, ktorych tak naprawde nie da sie przepisac potrafi czlowieka sprowadzic na ziemie i nauczyc pokory :)

  2. Widzialem wiele, ale mialem nadzieje, ze w kodach, niby dobrych programistow, nie znajde takich bubli jak using namespace xxx w globalnej przestrzeni nazw w naglowkach.

    Ja mam nadzieje, ze od stycznia, w nowej pracy nie bede za nadto przeklinal poprzedniego programisty ;p
    Choc troche sie boje ze mnie to nie minie ;)

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *