Posts tagged ‘NULL’

Zabezpieczenie przed NULL-em

2010-09-18 22:36

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 ify w rodzaju:

  1. if (!p) return false;
  1. if (arg == null) throw new ArgumentNullException("arg");

Kiedy jednak sprawdzenie nullowatości jest tylko częścią warunku, wtedy w ruch idzie zwykle “sztuczka” z leniwą ewaluacją (lazy evaluation):

  1. if (p && p->key == k) return p;

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 nulla na jakąś inną wartość domyślną:

  1. string str = s != null ? s : "N/A"; // C++/C#/Java
  1. s = s if s != None else "N/A" # Python

Tutaj C# oferuje specjalny operator ??, pomyślany właśnie na tego typu okazje:

  1. string str = s ?? "N/A";

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:

  1. int? x = null; // liczba typu int lub null
  2. int y = x ?? -1; // ustaw y = -1; jeśli x == null

Operator ?? przydaje się wtedy do konwersji na typ bazowy z określoną wartością domyślną.

Tags: , , ,
Author: Xion, posted under Programming » Comments Off on Zabezpieczenie przed NULL-em

Czym jest NULL?

2008-04-24 21:42

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:

  1. int n = NULL;

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:

  • może być podstawiona za dowolny typ wskaźnikowy
  • nie może być podstawiona za żaden inny typ, zwłaszcza liczbowy

To zaś da się osiągnąć, pisząc odpowiednią… klasę:

  1. #undef NULL
  2. const
  3.     class Null
  4.     {
  5.         public:
  6.             // konwersja na dowolny typ wskaźnikowy (także pointer-to-member)
  7.             template <typename T> operator T* () const { return 0; }
  8.             template <class C, typename T> operator T C::*() { return 0; }
  9.         private:
  10.             // zablokowanie pobierania adresu
  11.             void operator & () const;
  12.     }
  13. NULL;

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

Tags: ,
Author: Xion, posted under Programming » 10 comments
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.