Korzystanie z przestrzeni nazw w C++

tech • 979 słów • 5 minut czytania

Dziś chciałbym pomarudzić o tym jak to genialni programiści olewają i ignorują istnienie tak przydatnego i niezastąpionego wynalazku w C++ jakim są przestrzenie nazw, które rozwiązują problemy kolizji nazw.

Problem kolizji nazw jest szczególnie znany osobom programującym w C i innych językach, gdzie istnieje jedna globalna przestrzeń dla wszystkich nazw i identyfikatorów. Utrudnia to pisanie programów i odrębnych modułów. Do rozwiązania takiego problemu można użyć wiele różnych sposobów i mechanizmów. Jednym z nich (najpopularniejszym) jest stosowanie różnych wymyślnych prefiksów i sufiksów do definiowanych nazw zmiennych i funkcji. Co z kolei prowadzi do powstania długich i kłopotliwych nazw - takich potworków ze zlepków kilku słów lub skrótów.

Na szczęście w C++ możemy zapomnieć o tych potworkach i wyrażać się jasno - krótko, zwięźle i czytelnie. A wszystko poprzez zdefiniowanie przestrzeni nazw, czyli zasięgu, w którym dane nazwy będą obowiązywać. 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żywaną do włączenia przestrzeni nazw standardowej biblioteki. Nie jest to złe, ale co gorsza, najczęściej tę dyrektywę “wrzucają” odruchowo na samym początku pliku, tuż po włączeniu nagłówków. Często tłumacząc 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. To już 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 “importować” wszystkie nazwy do globalnej przestrzeni nazw. Oczywiście zabieg taki czasem się przydaje w pojedynczych jednostkach translacji (plikach cpp). Co ma miejsce 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 pliku nagłówkowym 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 to jest to tylko Twój problem i nic mi do tego. Ale gdy tworzysz oprogramowanie, które będzie wykorzystywane przez innych deweloperów to popełniłeś niewybaczalny błąd. Dyrektywa ta będzie oddziaływać także na kod programisty korzystającego z Twojego kodu. Poza zaśmieceniem jego globalnej przestrzeni nazw, może wprowadzić wiele problemów, nieraz tak ukrytych, że trudno będzie znaleźć przyczynę. Nikt Ci tego nie wybaczy. W najlepszym przypadku “oleją” Twój kod i skorzystają z konkurencji, i zapewne nigdy nie spróbują już skorzystać z jakiegokolwiek twojego genialnego kodu w przyszłości.

Myślałem, że wykorzystanie dyrektywy używania mogę spotkać jedynie w różnych kodach początkujących adeptów sztuki programowania. A popełnienie błędu z użyciem tego mechanizmu w nagłówkach raczej jest niemożliwe. Jednak się myliłem. Ostatnio natrafiłem całkiem przypadkiem na kod jakiegoś programisty, który to szczycił się dobrą znajomością C++ z takim bublem w kodzie.

Nasuwa się więc pytanie, jak dobrze korzystać z przestrzeni nazw. Ależ bardzo prosto - używać zawsze jawnego kwalifikowania nazwy. A jeśli z jakiś powodów jest to kłopotliwe, bądź przestrzenie są kilkukrotnie zagnieżdżone, to możemy wykorzystać deklaracje użycia tylko do nazw rzeczywiście nam potrzebnych, 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.

Przykładowo 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 krótko, 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 temat1:

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 złej praktyki związanej z wykorzystaniem dyrektywy używania, nie tylko przez początkujących, ale i czasem przez już nieco doświadczonych programistów. W wielu przypadkach jest to zły nawyk, który może być źródłem wielu błędów i nie tylko w kodzie autora.

Przestrzenie nazw są wspaniałym narzędziem i powinniśmy z nich korzystać, najczęściej do organizowania naszego kodu i jego logicznej struktury. 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ą.

Mechanizm przestrzeni nazw w C++ wydaje się bardzo prosty i taki w rzeczywistości jest, choć posiada dużo większe 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 programiści C++ być może nie słyszeli. Innym ciekawym narzędziem są anonimowe przestrzenie nazw, nieco różniące się od tych zwykłych nazwanych (może będę mila okazje o nich niedługo poopowiadać). Trzeba też pamiętać, że deklaracja używania pełni również jakieś funkcje w klasach, dziedziczeniu i wzorach.


Przypisy

  1. B. Stroustrup: “Projektowanie i rozwój języka C++”, Wydawnictwa Naukowo-Techniczne 1996 ↩︎

Komentarze (4)

Fanael avatar
Fanael
20091211-140329-fanael

Ta literówka w tytule to tak celowo? :>

Malcom avatar
Malcom
20091211-141029-malcom

Literówki są już u mnie chyba cechą charakteru :P

Maciek avatar
Maciek
20091212-001020-maciek

Myślałem, że 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 :)

Malcom avatar
Malcom
20091212-002659-malcom

Widziałem już wiele, ale miałem złudną nadzieję, że w kodach niby dobrych programistów nie znajdę takich bubli jak using namespace xxx w globalnej przestrzeni nazw i to w nagłówkach.

Ja mam nadzieje, że od stycznia w nowej pracy nie będę za nadto przeklinał poprzedniego programisty. Choć trochę się boję, że mnie to nie ominie ;)

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/