Defensywne programowanie wymaga, by zabezpieczać się przed różnymi niepożądanymi sytuacjami. Jedną z częstszych jest próba odwołania się do obiektu czy wartości, która nie istnieje – czyli np. dereferencja wskaźnika pustego czy użycie odwołania zawierającego null
. Stąd bardzo częste if
y w rodzaju:
Kiedy jednak sprawdzenie null
owatości jest tylko częścią warunku, wtedy w ruch idzie zwykle “sztuczka” z leniwą ewaluacją (lazy evaluation):
Większość języków ją dopuszcza, jako że jest ona przy okazji pewną formą optymalizacji. Jeśli bowiem pierwszy argument operatora logicznego daje informację o prawdziwości/fałszywości całego wyrażenia, nie trzeba już wyliczać drugiego.
Inną typową sytuacją jest zamiana null
a na jakąś inną wartość domyślną:
Tutaj C# oferuje specjalny operator ??
, pomyślany właśnie na tego typu okazje:
Działa on dobrze z typami Nullable
, czyli specyficznym rodzajem typów pochodnych, które dopuszczają wartość null
tam, gdzie typ macierzysty jej nie przewiduje:
Operator ??
przydaje się wtedy do konwersji na typ bazowy z określoną wartością domyślną.
Gdybym chciał byś złośliwy, to stwierdziłbym, że w C++ nawet ‘nic’ (czyli NULL
) nie jest takie, jak być powinno. Ale ponieważ w rzeczywistości jestem wcieleniem łagodności (;]), napiszę raczej o tym, jak można zaradzić na niedogodności obecnej postaci wskaźnika pustego w C++.
Cały problem z NULL
-em polega na tym, że nie jest on wartością odpowiedniego typu. Dwie klasyczne definicje tej stałej – jako 0
lub (void*)0
– mają zauważalne mankamenty. Pierwsza definiuje NULL
jako liczbę, co sprawia, że dozwolone są bezsensowne podstawienia w rodzaju:
Natomiast druga nie da się wprawdzie skonwertować na typ liczbowy, ale nie da się też niejawnie zmienić w wartość żadnego innego typu wskaźnikowego niż void*
. A tego oczekiwalibyśmy po wskaźniku pustym. Dlatego z dwojga złego w standardzie przyjęto pierwszą definicję i NULL
jest po prostu zerem.
To, czego naprawdę byśmy chcieli po NULL
, to wartość 0
, która:
To zaś da się osiągnąć, pisząc odpowiednią… klasę:
Sztuczką są tu oczywiście szablony operatorów konwersji. Zapewniają one możliwość traktowania naszego NULL
-a jako wartości dowolnego typu wskaźnikowego – łącznie ze wskaźnikami do składowych klas. A ze względu na brak innych konwersji, nie jest możliwe automatycznie przypisanie naszego pustego wskaźnika do zmiennej liczbowej.
Taki NULL
jest na tyle dobry, że działa nawet z uchwytami Windows API, gdyż wewnętrznie są one zdefiniowane jako specyficzne wskaźniki. Dziwi więc, dlaczego nadal nie ma go chociażby w bibliotece standardowej C++. Najwyraźniej nawet jeśli chcemy mieć ‘nic’, trzeba się trochę napracować ;P