GeeksforGeeks
On 16 stycznia, 2022 by adminOświadczenie using namespace std jest ogólnie uważane za złą praktykę. Alternatywą dla tego stwierdzenia jest określenie przestrzeni nazw, do której należy identyfikator za pomocą operatora zakresu(::) za każdym razem, gdy deklarujemy typ.
Pomimo, że ta instrukcja oszczędza nam wpisywania std:: za każdym razem, gdy chcemy uzyskać dostęp do klasy lub typu zdefiniowanego w przestrzeni nazw std, importuje ona całą przestrzeń nazw std do bieżącej przestrzeni nazw programu. Weźmy kilka przykładów, aby zrozumieć, dlaczego to może nie być taka dobra rzecz
Powiedzmy, że chcemy użyć cout z przestrzeni nazw std. Piszemy więc
Przykład 1:
#include <iostream>
using
. namespace
std;
cout <<
" Something to Display"
;
Teraz na późniejszym etapie rozwoju, chcemy użyć innej wersji cout, która jest zaimplementowana w jakiejś bibliotece o nazwie „foo” (na przykład)
#include <foo.h>
#include <iostream>
using
namespace
std;
cout <<
" Something to display"
;
Zwróć uwagę, jak jest dwuznaczność, do której biblioteki wskazuje cout? Kompilator może to wykryć i nie skompilować programu. W najgorszym przypadku program może się skompilować, ale wywołać niewłaściwą funkcję, ponieważ nigdy nie określiliśmy, do jakiej przestrzeni nazw należy identyfikator.
Przestrzenie nazw zostały wprowadzone do C++, aby rozwiązać konflikty nazw identyfikatorów. Zapewniało to, że dwa obiekty mogą mieć taką samą nazwę, a mimo to być traktowane inaczej, jeśli należą do różnych przestrzeni nazw. Zauważ, że w tym przykładzie nastąpiło coś dokładnie odwrotnego. Zamiast rozwiązywać konflikt nazw, w rzeczywistości tworzymy konflikt nazw.
Kiedy importujemy przestrzeń nazw, w zasadzie wciągamy wszystkie definicje typów do bieżącego zakresu. Przestrzeń nazw std jest ogromna. Ma setki predefiniowanych identyfikatorów, więc jest możliwe, że programista może przeoczyć fakt, że istnieje inna definicja ich zamierzonego obiektu w bibliotece std. Nieświadomy tego może przystąpić do określenia swojej własnej implementacji i oczekiwać, że będzie ona używana w późniejszych częściach programu. W ten sposób istniałyby dwie definicje tego samego typu w bieżącej przestrzeni nazw. Nie jest to dozwolone w C++, a nawet jeśli program się skompiluje, nie ma sposobu, aby dowiedzieć się, która definicja jest używana gdzie.
Rozwiązaniem problemu jest jawne określenie, do której przestrzeni nazw należy nasz identyfikator za pomocą operatora zakresu (::). Zatem jednym z możliwych rozwiązań powyższego przykładu może być
#include <foo>
#include <iostream>
std::cout <<
"Something to display"
;
foo::cout <
"Something to display"
;
Ale konieczność wpisywania std:: za każdym razem, gdy definiujemy typ, jest uciążliwa. Powoduje to również, że nasz kod wygląda bardziej włochato z dużą ilością definicji typów i utrudnia czytanie kodu. Rozważmy na przykład kod do uzyskania aktualnego czasu w programie
Przykład 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);
Kod źródłowy, który jest zaśmiecony skomplikowanymi i długimi definicjami typów, nie jest zbyt łatwy do odczytania. Jest to coś, czego programiści starają się unikać, ponieważ utrzymanie kodu jest dla nich najważniejsze.
Jest kilka sposobów, aby rozwiązać ten dylemat, tj. określić dokładną przestrzeń nazw bez zaśmiecania kodu słowami kluczowymi std.
Rozważmy użycie typedefów
typedefy uchronią nas przed pisaniem długich definicji typów. W naszym przykładzie 1, moglibyśmy rozwiązać problem używając dwóch typedefów jeden dla biblioteki std i drugi dla 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"
;
Zamiast importować całe przestrzenie nazw, zaimportuj okrojoną przestrzeń nazw
W przykładzie 2 mogliśmy zaimportować tylko przestrzeń nazw chrono pod 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);
Możemy również użyć tej deklaracji do importu pojedynczego identyfikatora. Aby zaimportować tylko std::cout moglibyśmy użyć
using std::cout;
Jeśli nadal importujesz całe przestrzenie nazw, staraj się to robić wewnątrz funkcji lub w ograniczonym zakresie, a nie w zakresie globalnym.
Używaj instrukcji „using namespace std” wewnątrz definicji funkcji lub klas, definicji struktur. W ten sposób definicje przestrzeni nazw są importowane do zakresu lokalnego, a my przynajmniej wiemy, skąd mogą pochodzić ewentualne błędy, jeśli się pojawią.
.
#include <isotream>
using
namespace
std;
void
foo()
{
using
namespace
std;
}
Wnioski.
Przedyskutowaliśmy alternatywne metody dostępu do identyfikatora z przestrzeni nazw. We wszystkich przypadkach należy unikać importowania całych przestrzeni nazw do kodu źródłowego.
Choć dobre praktyki kodowania mogą wymagać trochę czasu na naukę i rozwój, na ogół opłacają się na dłuższą metę. Pisanie czystego, jednoznacznego i solidnego kodu wolnego od błędów powinno być intencją każdego programisty.
Dodaj komentarz