W większości języków możemy zdefiniować nową nazwę dla istniejącego typu danych; nazywa się ją zwykle aliasem. I tak na przykład w C/C++ jest to możliwe za pomocą słowa kluczowego typedef
. Analogicznie w Delphi mamy od tego słowo kluczowe type
:
[delphi]type TMyInt = Integer;[/delphi]
Tak powstały typ TMyInt
jest faktycznie tylko aliasem. Zmienne należące do tego typu są bowiem całkowicie kompatybilne ze zmiennymi zwykłego typu całkowitego Integer
. W razie potrzeby konwersja między nimi może bez problemu zachodzić w obie strony.
Jeżeli jednak użylibyśmy deklaracji w formie:
[delphi]type TMyInt = type Integer;[/delphi]
wówczas TMyInt
byłby już zupełnie innym typem niż Integer
. Mimo że oba mogłyby przechowywać wartości tego samego rodzaju (liczby całkowite) i z tego samego zakresu, konwersje między nimi wymagałyby rzutowania.
Można by sądzić, że tworzenie takich typów rozróżnialnych “na siłę” jest bezcelowe. Zauważmy jednak, że typy wyliczeniowe (deklarowane przez enum
) są przecież także w gruncie rzeczy liczbami z określonego zbioru. Najczęściej jednak nie chcemy, aby możliwa była niejawna konwersja między nimi a normalnymi typami liczbami. Wszystko dlatego, że w enumach liczby nie pełnią funkcji liczb, tylko identyfikatorów pewnych stanów.
Podobnie tutaj w przypadku TMyInt
nie chodzi nam zapewne o liczby w sensie ich wartości, tylko o coś w rodzaju uchwytów – identyfikatorów obiektow. Kopalnią typów przeznaczonych do takiego właśnie celu jest oczywiście Windows API, zawierające tak znane i lubiane typy jak HWND
, HINSTANCE
, HDC
, itd. Wszystkie one są w gruncie rzeczy liczbami 32-bitowymi, a mimo to nie są ze sobą kompatybilne. Gdyby API to było obiektowe, obiekty reprezentowane obecnie przez te uchwyty należałyby do różnych klas.
Efekt niezgodności uchwytów osiągnięto, deklarując ich typy nie jako aliasy na DWORD
:
lecz jako niezwiązane ze sobą typy wskaźnikowe:
Można to uznać za dość pokrętną sztuczkę, ale na pewno jest ona lepsza niż tworzenie typu wyliczeniowego zawierającego nieco ponad 4 miliardy (232) nazwanych stałych ;]
Ja dawno temu też miałem taką potrzebę.
struct STag1 {};
struct STag2 {};
typedef CSubstring SxString_t;
typedef CSubstring SxKey_t;
Dzięki takiemu zapisowi, przy wywoływaniu ‘strumieniowania’ moglem zdecydować, czy chce dodawac łańcuch zanków – traktowany jako klucz czy jako string. :)
Wreszcie zrozumiałem sens takich konstrukcji. Zawsze mnie dziwiło jak ktoś tak sobie ‘utrudniał’ zycie. thx