Część I
W części I zaczniesz naukę pisania programów Windows w Delphi. Pierwsze trzy rozdziały omawiają podstawy języka Object Pascal. Napiszesz proste programy testowe, które utrwalą Twoje wiadomości na temat poszczególnych możliwości języka Object Pascal. Uprzedzam jednak, że nie będą one prawdopodobnie tego typu programami, dla których pisania kupiłeś Delphi. Nie będą miały żadnych fontann i wodotrysków. Przypuszczalnie nie będziesz pod ich wrażeniem. Jednak te programy pomogą Ci zrozumieć podstawy Object Pascala.
W rozdziale 4. zaczniesz naukę niektórych rzeczy, które sprawiły, że Delphi jest tak wspaniałym narzędziem. Dowiesz się o Delphi IDE i jak go użyć do stworzenia programu Windows. Zanim skończysz rozdział 4., zbudujesz swój pierwszy, prawdziwy program Windows.
W rozdziale 5. omówię biblioteki klas i pokażę jak je możesz wykorzystać. Dowiesz się tam o właściwościach, metodach i zdarzeniach, które są zasadniczą częścią programowania w Delphi. W rozdziale 6. poznasz nowe szczegóły dotyczące Delphi IDE, dzięki czemu będziesz wiedział jak działa cały Delphi IDE i w jaki sposób ułatwia pisanie programów. Od tego momentu wszystko stanie się bardziej interesujące.
W rozdziale 6. opiszę niektóre z komponentów VCL, które będziesz najczęściej używał podczas pisania programów w Delphi. Dowiesz się o poszczególnych komponentach oraz jak ich używać.
Zanim skończysz tę część spędzisz oczywiście dużo czasu na czytaniu. Mam jednak nadzieję, że spędzisz też wiele czasu na ćwiczeniu. Czytanie tej książki to nie wyścig. Pierwsza osoba, która ją skończy, nie dostanie nagrody. Podczas nauki programowania lepiej być żółwiem niż zającem. Poświęć trochę czasu na ćwiczenia. Doświadczenie jest najlepszym nauczycielem. Jeżeli więc jesteś gotowy na to, aby zacząć - obróć stronę, a Twoja wyprawa ku umiejętności programowania w Delphi będzie rozpoczęta!
Rozdział 1.
Zaczynamy
Moje gratulacje - wybrałeś najlepiej dziś oceniane narzędzie programowania. Zanim jednak zaczniesz używać tego wszystkiego, co Delphi ma do zaoferowania, musisz poświęcić trochę czasu na zapoznanie się z IDE i z językiem Object Pascal. Tematami tego rozdziału będą:
Krótki przegląd możliwości Delphi
Wprowadzenie do języka Object Pascal
Moduły (ang. units), zmienne i typy danych
Tablice
Łańcuchy (ang. strings)
Czym właściwie jest Delphi?
Wiesz już, że Delphi jest najlepiej sprzedającym się narzędziem do szybkiego tworzenia aplikacji (ang. RAD - Rapid Application Development) dla Windows. Przy pomocy Delphi możesz tworzyć programy dla Windows szybciej i łatwiej, niż było to kiedykolwiek możliwe. Możesz tworzyć aplikacje działające w trybie terminalowym, a także aplikacje działające w trybie graficznym (GUI). Napisane przez Ciebie aplikacje Win32 GUI wykorzystują wszystkie zalety kompilowanego do kodu maszynowego języka Object Pascal wkomponowanego w otoczenie RAD. Oznacza to, że możesz projektować interfejs użytkownika (menu, okna dialogowe, okno główne itd.) korzystając z techniki „przeciągnij i upuść”. Możesz m. in. umieszczając na formularzach kontrolki ActiveX tworzyć wyspecjalizowane programy (np. przeglądarki WWW) w ciągu kilku minut. Nie tracisz przy tym nic z szybkości wykonywania programu - Delphi generuje bardzo efektywny kod wynikowy.
Jakbym już słyszał Cię mówiącego: „To naprawdę świetne narzędzie!” I wiesz co? - masz rację. Jednak zanim zapalisz się, aby pisać od razu duże i skomplikowane aplikacje, muszę Cię uprzedzić, że najpierw trzeba zainwestować trochę czasu w naukę programowania w Pascalu. Nie jest niestety prawdą twierdzenie, że wystarczy kupić taki produkt jak Delphi i można stać się mistrzem programowania w Windows w ciągu jednej nocy. Na to trzeba solidnie zapracować. Delphi umożliwia ukrycie bardzo wielu niskopoziomowych szczegółów, które de facto są szkieletem każdego programu w Windows, jednak nie stworzy ono niczego za Ciebie. Aby pisać jakiekolwiek programy, nawet w Delphi, trzeba być programistą. A to oznacza, że trzeba nauczyć się programowania. Może to być czasami droga długa i trudna. Nie martw się jednak na zapas - Delphi może sprawić, że przejdziesz przez ten etap w miarę bezboleśnie. Można się nawet przy tym nieźle bawić.
Rzut oka na Delphi IDE
Ten rozdział zawiera krótki opis środowiska IDE (ang. Integrated Development Environment). Szerzej tym tematem zajmę się w rozdziale 4. „IDE - środowisko programisty”. Przypuszczam, że jesteś na tyle zaawansowany w Windows, że wiesz, jak się uruchamia Delphi. Po pierwszym uruchomieniu programu IDE wygląda mniej więcej tak, jak na rysunku 1.1.
Rysunek 1.1. Środowisko zintegrowane IDE |
|
W górnej części ekranu jest główne okno IDE. Zawiera ono pasek narzędzi i paletę komponentów. Pasek narzędzi umożliwia szybki dostęp do najczęściej stosowanych opcji takich jak otwieranie, zapisywanie i kompilacja projektu. Paleta komponentów zawiera duży wybór podzielonych tematycznie komponentów (np. etykiet, pól edycyjnych, przycisków itd.), które można przenosić na formularz za pomocą myszy. Na palecie jest cały szereg zakładek, dzięki którym znalezienie określonego komponentu jest łatwiejsze i szybsze. Aby umieścić komponent na formularzu, trzeba po prostu kliknąć na odpowiadający mu symbol na palecie komponentów, a następnie kliknąć na formularzu w miejscu, gdzie się chce ten komponent umieścić. Nie przejmuj się tym, że na razie nie wiesz, jak tych komponentów używać. Nauczysz się tego w swoim czasie.
|
Komponent jest samodzielnym elementem biblioteki wykonującym określoną predefiniowaną funkcję. Komponentem jest np. etykieta, pole edycji czy lista wyboru. |
Inspektor Obiektów
Poniżej głównego okna w lewej części ekranu jest tzw. Inspektor Obiektów. Za jego pomocą możesz modyfikować właściwości i zdarzenia poszczególnych komponentów. W czasie swojej pracy z Delphi będziesz używać go praktycznie bez przerwy. Inspektor Obiektów podzielony jest na dwie części: kartę Properties (właściwości) i kartę Events (zdarzenia). Właściwości komponentu określają jego wygląd i sposób działania. Na przykład zmiana właściwości color jakiegoś komponentu powoduje zmianę koloru jego tła. Zestaw dostępnych właściwości zmienia się w zależności od komponentu, aczkolwiek wszystkie komponenty mają kilka wspólnych właściwości, np. Width i Height.
|
Właściwość (ang. property) określa zachowanie się komponentu. |
Karta Events zawiera listę zdarzeń komponentu. Zdarzenie zachodzi, gdy użytkownik lub system wykonuje jakąś akcję dotyczącą komponentu. Na przykład kiedy użytkownik kliknie myszą w obszarze komponentu, generowane jest zdarzenie informujące, że kliknięto w obszarze komponentu. Możesz napisać procedurę obsługi takiego zdarzenia, tzn. procedurę, która wykonuje określone akcje, jeżeli to zdarzenie wystąpi. Podobnie jak właściwości, zestawy zdarzeń są różne dla różnych komponentów.
|
Zdarzenie zachodzi w wyniku interakcji komponentu z użytkownikiem lub z systemem. |
|
Procedura obsługi zdarzenia (ang. event handler) jest fragmentem kodu, który wywoływany jest w wyniku zajścia tego zdarzenia. |
Przestrzeń robocza
Główną część Delphi IDE stanowi przestrzeń robocza. Bezpośrednio po uruchomieniu pakietu na przestrzeni roboczej umieszczony jest Projektant Formularzy. Jak sama nazwa wskazuje, służy on do projektowania formularzy. W Delphi formularz jest po prostu oknem wyświetlonym na ekranie po uruchomieniu skompilowanego programu. Główne okno programu, okna dialogowe itp. są formularzami. Projektant Formularzy umożliwia umieszczanie, przesuwanie i skalowanie komponentów umieszczanych na formularzach. Pod Projektantem Formularzy ukryty jest Edytor Kodu. Wpisuje się w nim i edytuje kod tworzonego programu. Wszystkie elementy IDE - Inspektor Obiektów, Projektant Formularzy, Edytor Kodu i Paleta Komponentów - ściśle ze sobą współpracują w czasie tworzenia aplikacji.
Teraz, gdy poznałeś podstawowe elementy środowiska Delphi, czas na napisanie pierwszego programu.
Twój pierwszy program: Hello World
W prawie każdej książce o programowaniu pierwszy prezentowany program wypisuje na ekranie tekst „Hello World”. To już tradycja, której nie wypada (i ja też nie zamierzam) się sprzeciwić. Myślę zresztą, że będzie to dobry przykład do zilustrowania łatwości budowania aplikacji dla Windows za pomocą Delphi. Nic nie oferuje szybszego sposobu na napisanie aplikacji wypisującej na ekranie Hello World, niż Delphi.
Tworzenie programu
Jeżeli jeszcze tego nie zrobiłeś, uruchom teraz Delphi. Na ekranie powinieneś zobaczyć czysty formularz o nazwie Form1. Nazwy formularzy mają duże znaczenie w Delphi, opowiem jednak o tym szczegółowo trochę później. Po lewej stronie formularza, w Inspektorze Obiektów, przedstawione są właściwości tego formularza. Kliknij na pasku tytułowym Inspektora Obiektów. Właściwość Caption jest podświetlona, kursor ustawiony jest w polu edycji tytułu formularza (jeżeli nie widać właściwości Caption, należy jej poszukać przesuwając pionową belkę w Inspektorze Obiektów. Właściwości uporządkowane są alfabetycznie). Wpisz tekst Hello World! w pole edycji właściwości Caption.
|
Gdy zmodyfikujesz jakąś właściwość, Delphi natychmiast uwzględni tę zmianę odpowiednio do rodzaju właściwości. W czasie wpisywania nowego tytułu zauważ, że tytuł formularza zmienia się w trakcie pisania. |
Teraz kliknij przycisk „Run” (zielona strzałka na pasku narzędzi). Możesz również uruchomić program naciskając klawisz F9 lub wybierając z menu Run opcję Run. Po uruchomieniu programu ukaże się na ekranie okno zatytułowane Hello World!. W naszym programie główne okno jest prawie takie samo, jak pusty formularz (jest mała różnica - na projektancie formularzy widać siatkę, której nie ma w oknie działającego programu). Właściwie na pierwszy rzut oka trudno zauważyć, że program jest uruchomiony, ponieważ jego okno jest w tym samym miejscu i tej samej wielkości, co formularz w Projektancie Formularzy. Moje gratulacje - właśnie napisałeś swój pierwszy program dla Windows! Nie było to trudne, prawda?
Można by powiedzieć, że nie jest to nic wielkiego. To prawda, ale jednak jest to prawdziwy program dla Windows. Sprawdź sam. Okno programu można przesuwać przeciągając pasek tytułowy, można je minimalizować, maksymalizować, można je zamknąć klikając na przycisk w prawym górnym rogu. Można nawet znaleźć ten program przy pomocy Eksploratora Windows (prawdopodobnie będzie w katalogu \Delphi40\bin jako Project1.exe) i uruchomić przez podwójne kliknięcie.
Modyfikacja programu
No dobrze, może wyświetlenie Hello World na pasku tytułowym programu nie było najelegantszym rozwiązaniem. Spróbujmy to trochę ulepszyć. Jeżeli ciągle program Hello World masz uruchomiony, zamknij go klikając ikonę umieszczoną w prawym górnym rogu jego okna. Pokaże się znowu Projektant Formularzy umożliwiając modyfikację formularza (i tym samym modyfikację programu).
Aby uczynić program trochę bardziej realnym, dodaj jakiś tekst w centrum okna. W tym celu należy na środku formularza umieścić etykietę:
Najpierw kliknij na karcie Standard na Palecie Komponentów. Trzeci komponent licząc od lewej oznaczony jest literą A. Jeżeli umieścisz kursor myszy nad tym przyciskiem, ukaże się małe okienko z napisem Label.
Kliknij na tym komponencie, a następnie kliknij gdzieś w obszarze formularza. Etykieta zostanie umieszczona na formularzu z przypisaną do właściwości Caption wartością Label1.
Zwróć uwagę na Inspektora Obiektów. Pokazuje on teraz właściwości komponentu Label1 (poprzednio widać było na nim właściwości komponentu Form1). Właściwość Caption jest wyróżniona.
Kliknij na pasku tytułowym Inspektora Obiektów albo na właściwości Caption i wpisz tam Hello World! Teraz na etykiecie na formularzu widać napis Hello World!.
Teraz, kiedy etykieta jest aktywna, możesz także zmienić rozmiar czcionki. Kliknij podwójnie na właściwości Font. Rozwinie się dodatkowe drzewo atrybutów czcionki.
Znajdź właściwość Size i zmień wielkość czcionki na 24 (teraz jest ustawiona na 8). Kiedy tylko naciśniesz klawisz Enter lub klikniesz gdziekolwiek na formularzu etykieta zmieni swój rozmiar.
Prawdopodobnie napis nie jest umieszczony na środku formularza. Aby go przesunąć, po prostu kliknij na nim i przeciągnij go na środek. Po zakończeniu tej operacji można powtórnie skompilować i uruchomić program. Kliknij ponownie przycisk Run, a po krótkiej chwili program się uruchomi. Teraz widać piękny napis Hello World umieszczony na środku okna i na pasku tytułowym. Na rysunku 1.2 przedstawione jest okno uruchomionego programu Hello World.
Rysunek 1.2. Okno uruchomionego programu Hello World |
|
Zamknięcie projektu
Teraz, kiedy już „liznąłeś” trochę Delphi, stwierdziłeś pewnie, że programowanie w Windows z pomocą Delphi jest o wiele bardziej interesujące niż było w starych, dobrych czasach. Aby przygotować się na następne wyzwania, musisz zamknąć bieżący projekt w IDE. Wybierz File | Close All z głównego menu. Na pytanie, czy zachować zmiany w projekcie Project1 odpowiedz przecząco chyba, że zamierzasz w przyszłości jeszcze z nim poeksperymentować.
Twój drugi program: Hello World II
Zanim zaczniesz uczyć się Pascala musisz dowiedzieć się trochę więcej o tym, jak Delphi właściwie działa. Wykorzystasz tę wiedzę później, przerabiając kilka następnych rozdziałów.
Tworzenie programu Hello World II
Twoim celem jest napisanie programu, który wyświetla napis Hello World II po naciśnięciu przycisku. Dzięki temu nauczysz się również uruchamiać programy, których urywki będę prezentował w kilku następnych rozdziałach.
Wykonaj następujące czynności:
Wybierz opcję File | New Application z głównego menu, aby otworzyć nowy projekt (odpowiedz „Nie” na pytanie, czy zachować zmiany w bieżącym projekcie).
Kliknij kartę Standard na Palecie Komponentów, następnie kliknij na ikonie z rysunkiem klawisza z napisem OK. (komponent Button).
Umieść kursor gdziekolwiek na formularzu i kliknij. Ukaże się w tym miejscu przycisk z napisem Button1.
W ten sam sposób umieść komponent Label mniej więcej na środku formularza.
Formularz na ekranie powinien wyglądać mniej więcej tak, jak na rysunku 1.3. Zwróć uwagę, że etykieta ma właściwość Caption ustawioną na domyślną wartość Label1, a przycisk - na Button1.
Rysunek 1.3. Wygląd formularza po umieszczeniu na nim przycisku i etykiety |
|
Modyfikacja programu Hello World II
W pierwszej wersji programu Hello World użyłeś Inspektora Obiektów do zmiany właściwości Caption etykiety. Było to zrobione na etapie projektowania aplikacji, a efekt widoczny był natychmiast po uruchomieniu programu. W tym ćwiczeniu zmienisz tekst na etykiecie bezpośrednio w kodzie programu.
|
O zmianie właściwości komponentów za pomocą Inspektora Obiektów i Projektanta Formularzy mówimy, że jest to zmiana na etapie projektowania. O zmianie natomiast właściwości komponentów z poziomu kodu, który wykonuje się w czasie pracy programu mówimy, że jest to zmiana na etapie wykonania (z poziomu kodu). |
Chcąc sprawić, żeby właściwość Caption etykiety umieszczonej na formularzu zmieniła się dopiero po naciśnięciu przycisku, należy zrobić co następuje:
Kliknij podwójnie na przycisku umieszczonym na formularzu. W wyniku tego Delphi wyodrębni w kodzie programu procedurę obsługi zdarzenia OnClick dla tego przycisku. Wygenerowana procedura będzie wygląda następująco
Procedure TForm1.Button1Click(Sender: TObject);
begin
end;
W tej chwili nie musisz się przejmować deklaracją ani parametrami tej procedury. Jedyne, co musisz wiedzieć to to, że procedura OnClick będzie wywoływana w czasie działania programu za każdym razem, kiedy użytkownik kliknie na tym przycisku. Kursor umieszczony zostaje automatycznie w linii między begin i end. Wpisz w tym miejscu:
Label1.Caption := 'Hello World II';
Dobrą praktyką jest stosowanie wcięć podczas pisania programu. Dzięki temu listingi stają się znacznie czytelniejsze, zwłaszcza gdy są długie i skomplikowane.
Ostatecznie procedura obsługi zdarzenia wygląda następująco:
Procedure TForm1.Button1Click(Sender: TObject);
begin
Label1.Caption := 'Hello World II';
end;
Jest to w sumie bardzo proste. Procedura ta przypisuje wartość Hello World II właściwości Caption etykiety Label1. Dzięki temu na formularzu w miejscu umieszczenia etykiety pokazuje się napis Hello World II.
Teraz kliknij przycisk Run na pasku narzędzi. Po uruchomieniu programu na środku okna widać wciąż napis Label1. Kliknij na przycisku, a napis zmieni się na Hello World II. Niezłe, prawda? Tak właśnie działa Delphi.
Czeka Cię niedługo sporo takich ćwiczeń, więc nabierzesz niezłego doświadczenia w umieszczaniu etykiet, przycisków i innych komponentów na formularzach. Nie wyjaśni to od razu wszystkich ukrytych mechanizmów działania, lecz na to przyjdzie czas później.
Przegląd języka Object Pascal
Zanim poznasz i opanujesz Delphi jako narzędzie RAD, musisz najpierw dobrze zaznajomić się z podstawami, czyli z językiem Object Pascal. Nie będzie to może najciekawsza część książki, ale niestety - bez tego nie da rady.
Nie bardzo można uczyć języka Object Pascal w sposób zorganizowany i uporządkowany, wszystkie jego elementy bowiem są ze sobą wzajemnie powiązane. Postaram się kolejno odkrywać elementy tej układanki i tworzyć z nich powoli obraz całości.
Pod koniec rozdziału 3. powinieneś mieć już całkiem niezłe pojęcie o Object Pascalu. Nie przejmuj się, jeżeli nie uda Ci się opanować wszystkiego od razu. Potrzeba do tego troszkę doświadczenia, które przyjdzie bardzo szybko.
W następnych rozdziałach będę przytaczał krótkie wycinki kodu ilustrujące poszczególne elementy języka. Będziesz wykonywał także ćwiczenia, aby pogłębić i ugruntować nowo zdobytą wiedzę. Pełny kod aplikacji będących tematem ćwiczeń możesz ściągnąć sobie z Internetu pod adresem http://www.mcp.com/info (jako ISBN należy wpisać
0-672-31286-7).
Trochę historii
Mniej więcej w roku 1984 Borland zaczął prace nad narzędziem RAD pod roboczą nazwą Delphi. Kiedy zdecydowano, że najlepszym modelem do realizacji RAD będzie architektura oparta na komponentach, trzeba było wybrać jakiś język programowania, na którym bazowałby cały system.
W tym czasie Borland znany był jako producent najlepszych narzędzi i kompilatorów Pascala. Jeżeli programowałeś w Pascalu, prawdopodobnie używałeś którejś wersji Turbo Pascala. Pascal zawsze był mocno związany z Borlandem. Mimo, że Borland nie „posiadał” Pascala w sensie prawnym, wprowadzał często do niego nowe standardy i rozszerzenia. Co więcej, nigdy nie było żadnego komitetu standaryzacyjnego Pascala, nie istniały także żadne inne formalne standardy. Borland stworzył więc Delphi z Pascalem jako językiem bazowym (wewnętrzna nazwa robocza przekształciła się w oficjalną nazwę produktu).
Zanim Delphi w ogóle zaistniało, Borland wprowadził do Pascala znaczne ulepszenia tworząc w ten sposób nowy język programowania nazwany Borland Pascal. Można by powiedzieć, że Borland Pascal ma się tak do Pascala, jak C++ do C. Object Pascal posiadał już klasy, wchodząc tym samym do rodziny obiektowo zorientowanych języków programowania. Wraz z rozwojem Delphi wprowadzano do niego nowe elementy i słowa kluczowe, które umożliwiały pełną realizację modelu opartego na komponentach, jak np. published lub property. Object Pascal był modyfikowany stosownie do potrzeb jakie wynikały w trakcie projektowania Delphi. W rezultacie współpraca Delphi i Borland Pascala stała się niemal doskonała.
Modyfikacja języka Pascal mogła wydawać się śmiałym krokiem. Jednak w historii był już wcześniej taki przypadek. Otóż Microsoft przerobił oryginalny język BASIC tworząc w ten sposób VISUAL BASIC. Różnice między pierwowzorem i VISUAL BASIC-em są tak duże, że trudno je do siebie porównywać.
Borland modyfikując Pascala wziął na siebie spore ryzyko. Miał bądź co bądź sporo lojalnych klientów, którzy wcale nie musieli zaakceptować tych zmian. Rezultat okazał się jednak hitem, który zawojował rynek.
Nie pomylę się jeżeli stwierdzę, że Object Pascal jest bardzo silnym językiem programowania. Wiem, co mówię. Mam za sobą doświadczenia z C i C++ i tak jak inni programiści, początkowo patrzyłem na Delphi z pewną dozą sceptycyzmu. Przekonałem się jednak szybko, że z Object Pascalem można bardzo wiele zdziałać. Dla przeciętnego programisty nie ma właściwie żadnej różnicy między tymi dwoma językami programowania w sensie ich możliwości. Delphi ma tę zaletę, że jest silne i względnie łatwe do opanowania. Nie sugeruję w żadnym wypadku, że Object Pascal jest niepełnowartościowym językiem programowania. Krążyły opinie, że nie jest on tak „poważny” jak C, jednak nie jest to wcale prawdą, zwłaszcza w przypadku dzisiejszego Object Pascala.
Object Pascal pozwala w pełni wykorzystać zalety programowania obiektowo zorientowanego. OOP (ang. Object-Oriented Programming) nie jest li tylko modnym sloganem, pozwala ono bowiem tworzyć obiekty wykorzystane w projekcie, nad którym właśnie pracujesz, jak również w wielu następnych.
|
Programiści Delphi używają kilku określeń dla języka, którego używają. Prawidłową nazwą jest, wydaje się, Object Pascal. Inni mówią po prostu o Pascalu, inni z kolei twierdzą, że programują w Delphi. Ty zapewne wybierzesz sobie termin, który Ci będzie najbardziej odpowiadał. Ja jednak w tej książce będę używał określenia Object Pascal i Pascal naprzemiennie, rezerwując słówko Delphi dla określenia IDE i jego narzędzi. |
|
Obiekt, podobnie jak komponent opisany poprzednio, jest częścią programu wykonującą określone zadanie (wszystkie komponenty są obiektami, lecz nie wszystkie obiekty są komponentami - wyjaśnię to szczegółowo później). |
Obiekt udostępniony jest programiście w stopniu możliwie jak najmniejszym, co znacznie ułatwia jego wykorzystywanie. Wszystkie wewnętrzne mechanizmy, o których programista nie musi wiedzieć, są przed nim ukryte. Wszystko to zgodne jest z koncepcją programowania obiektowo zorientowanego. Umożliwia to „modułowe” podejście do programowania. Dzięki temu nie trzeba za każdym razem „wynajdywać koła”, czyli robić tego, co inni dawno już zrobili. Programy Delphi są silnie OOP-centryczne, gdyż w bardzo dużym stopniu wykorzystują komponenty. Gdy jakiś komponent zostanie raz stworzony (czy to wbudowany w Delphi, czy Twój własny), może być ponownie użyty w każdym następnym programie Delphi. Komponent taki może także stanowić bazę do stworzenia nowego komponentu o rozszerzonych możliwościach. Szczegóły implementacji komponentów są ukryte przed programistą, który może skupić się na idei swojego projektu. Obiekty i klasy szczegółowo omówione są w rozdziale 3. „Klasy i programowanie obiektowo zorientowane”.
Moduły
Programowanie to coś więcej niż tylko „wklepywanie” kodu. W ostatecznym rozrachunku jest ono złożeniem koncepcji programu, czyli projektu, i realizacji tej koncepcji, czyli implementacji. Kod, który wpisujesz, zapisywany jest w pliku tekstowym. Kompilator przetwarzając ten plik tłumaczy tekst źródłowy do kodu wykonywalnego zrozumiałego przez komputer. Plik tekstowy, który Delphi kompiluje do kodu maszynowego, nazywa się modułem (ang. unit).
|
Moduł jest plikiem tekstowym, który może być skompilowany do programu wykonywalnego. |
Typy modułów
Aplikacja Delphi z graficznym interfejsem użytkownika składa się z minimum dwóch modułów. Moduł główny projektu (ang. project source unit) zawiera tekst źródłowy projektu. Takie moduły mają rozszerzenie DPR. Możesz obejrzeć zawartość tego pliku wybierając opcję Project | View Source z menu. Zwykle nie modyfikuje się ręcznie tego pliku, gdyż ewentualne błędy, powstałe przy takiej modyfikacji, mogą uniemożliwić w ogóle kompilację całego projektu (niektóre zaawansowane techniki programistyczne wymagają jednak ręcznej modyfikacji pliku źródłowego projektu, lecz na tym etapie nie musisz się tym przejmować).
Drugim typem modułu, który zawsze występuje w aplikacji Delphi, jest moduł formularza głównego. Moduł formularza, jak sama nazwa wskazuje, zawiera kod źródłowy związany z formularzem. Pliki tego typu mają rozszerzenie PAS. Jest to najczęściej wykorzystywany typ modułu w programach Delphi. Aplikacja GUI będzie zawsze zawierać jeden moduł stowarzyszony z formularzem (okno główne programu), może także zawierać jeden lub kilka dodatkowych modułów stowarzyszonych z dodatkowymi formularzami. Na przykład aplikacja, która wyświetla oprócz okna głównego okno „O programie”, posiada moduł formularza głównego oraz moduł związany z formularzem „O programie”.
|
Zauważyłeś pewnie, że piszę często „aplikacja GUI” (Graphical User Interface). Trzeba bowiem rozróżnić między aplikacją GUI a aplikacją działającą w trybie terminalowym (konsoli). Aplikacje działające w trybie terminalowym są 32-bitowymi aplikacjami Windows uruchamianymi w oknie „Tryb MS-DOS”. Nie mają one formularza głównego, chociaż mogą mieć inne formularze. Mają za to jeden lub więcej modułów. |
Istnieje także trzeci typ modułów, które można stosować w aplikacjach Delphi. Zawierają one tylko kod źródłowy, który wywoływany jest z innych modułów w projekcie, i nie są związane z formularzami.
Budowa modułu
Moduły w Delphi muszą mieć pewien określony format (podobnie jak wszystkie inne pliki źródłowe w różnych językach programowania). Jest to niezbędne, aby kompilator mógł skompilować kod do postaci wykonywalnej.
Moduł główny projektu zawiera na początku słowo kluczowe program, po którym następuje nazwa modułu i blok kodu objęty słowami kluczowymi begin i end. Możesz sprawdzić, jak wygląda tekst źródłowy projektu, poprzez View | Project Source. Plik źródłowy domyślnego projektu Delphi przedstawiony jest na listingu 1.1.
|
Numery linii przedstawione na listingu nie są częścią kodu. Umieściłem je tam po to, aby móc się do nich odwoływać. W tej książce na niektórych listingach będzie numeracja linii, na niektórych zaś nie. Musisz wiedzieć, że w Pascalu nie numeruje się linii (w przeciwieństwie do niektórych innych języków programowania, np. BASIC-a). |
Listing 1.1. Kod źródłowy domyślnego projektu Delphi
01: program Project1;
02:
03: uses
04: Forms,
05: Unit1 in 'Unit1.pas' {Form1};
06:
07: {$R *.RES}
08:
09: begin
10: Application.Initialize;
11: Application.CreateForm(Tform1, Form1);
12: Application.Run;
13: end;
W linii 1. słowo program wskazuje na to, że jest to plik źródłowy modułu głównego projektu. Bezpośrednio za nim jest nazwa projektu (Delphi nadaje każdemu nowemu projektowi domyślną nazwę Project1, dopóki nie zachowasz go pod jakąś bardziej znaczącą nazwą. W linii 3 zaczyna się sekcja Uses. Wymienia się w niej nazwy wszystkich innych modułów, do których są odwołania w tym module.
W linii 7. jest dyrektywa kompilatora nakazująca mu dołączenie pliku zasobów projektu (ang. resources) . Pliki zasobów opisane są bardziej szczegółowo w rozdziale 8. „Tworzenie aplikacji w Delphi”.
Linia 9. zawiera słowo kluczowe begin, w linii 13. zaś jest słowo end. Zwróć uwagę, że po końcowym end modułu jest kropka (w module może być wiele bloków kodu ograniczonych słówkami begin i end, jednak end z kropką występuje tylko na końcu). Instrukcje w liniach 10., 11. i 12. odpowiedzialne są za inicjalizację aplikacji, utworzenie jej głównego formularza i uruchomienie aplikacji.
|
Słowa kluczowe begin i end oznaczają początek i koniec bloku kodu. Blok taki może obejmować od kilku linii do kilku tysięcy linii. W programach pisanych w Pascalu słowa te spotyka się bardzo często. Czytając tę książkę opanujesz bardzo szybko zasady ich stosowania. |
Zajmijmy się teraz innym typem modułu w Pascalu. Wybierz opcję File | New z głównego menu. Gdy otworzy się okno dialogowe, podwójnie kliknij ikonę Unit. Delphi utworzy nowy moduł i wyświetli odpowiadający mu kod w Edytorze Kodu (przedstawiony na listingu 1.2).
Listing 1.2. Nowo utworzony moduł
01: unit Unit2;
02:
03: interface
04:
05: implementation
06:
07: end.
Nie jest tego dużo. Na początku jest nazwa modułu poprzedzona słówkiem unit, a na końcu, tak jak na listingu 1.1, jest słowo end (z kropką).
Tekst na listingu 1.2 różni się od tego z listingu 1.1 tym, że posiada dwa dodatkowe słowa kluczowe: interface i implementation. Opiszę to później w rozdziałach zatytułowanych „Sekcja interface” i „Sekcja implementation”. Oprócz tego nie ma na listingu modułu Unit2 słowa begin w odróżnieniu od kodu modułu głównego, który musi mieć zarówno begin, jak i end.
Poniżej przedstawiony jest opis słów kluczowych używanych w modułach w Pascalu.
Sekcja uses
Zerknij na listing 1.1. W linii 3. jest tam słowo uses. Oznacza ono początek listy, w której wylicza się wszystkie moduły, od których zależny jest dany moduł. Na przykład, w linii 11.:
Application.CreateForm(TForm1, Form1);
jest odwołanie do informacji, której nie ma w module głównym. Procedura Application. CreateForm umieszczona jest w module Forms.pas, natomiast TForm1 i Form1 są w module głównego formularza, Unit1.pas. Sekcja uses informuje Delphi, gdzie ma szukać informacji potrzebnej do skompilowania danego modułu. Przypomnę - sekcja uses wygląda następująco:
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
Zwróć uwagę, że lista ta zawiera nazwy dwóch modułów: Forms i Unit1. Druga linia w tej sekcji jest trochę niestandardowa. Występuje ona tylko w pliku źródłowym modułu głównego i oznacza odwołanie do formularza zawartego w pliku Unit1.pas. Tekst w nawiasach klamrowych to tzw. komentarz. Komentarze nie są w żaden sposób interpretowane przez kompilator i służą tylko i wyłącznie do opisywania i zwiększenia czytelności kodu.
Wpisując moduły do sekcji uses kieruj się następującymi zasadami:
Początek listy musi być poprzedzony słowem uses.
Nazwy modułów muszą być oddzielone od siebie przecinkami.
Na końcu listy musi zawsze być średnik.
Przykładowo, poniższe dwie sekcje są dla kompilatora równoważne:
uses
Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
uses
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
StdCtrls;
W module może być dowolna liczba sekcji uses.
|
Gdy wybierasz z menu opcję File | Use Unit, Delphi dodaje wskazany moduł do listy uses samoczynnie. |
Sekcja interface
W sekcji tej deklaruje się identyfikatory, które mają być widoczne na zewnątrz modułu (w innych modułach odwołujących się do tego modułu, czyli mających jego nazwę na swojej liście uses). Mogą to być identyfikatory klas, procedur, funkcji lub zmiennych.
Sekcja implementation
|
Sekcja implementation zawiera właściwy kod modułu. |
Sekcja ta zaczyna się słowem kluczowym implementation, a kończy się kolejnym słowem kluczowym modułu. Zwykle jest nim kończące end, ale może być to także słowo initialization w modułach, które posiadają specjalną sekcję inicjalizacyjną (o niej później).
Myślę, że najlepiej będzie wyjaśnić to na przykładzie. Powiedzmy, że umieszczasz w module Unit2 procedurę JakasProcedura i chcesz, żeby można się było do niej odwoływać z innych modułów w projekcie. Musisz więc zadeklarować procedurę JakasProcedura w sekcji interface, a definicję tej procedury umieścić w sekcji implementation. Kod modułu wyglądałby jak na listingu 1.3.
Listing 1.3. Moduł z procedurą publiczną
unit Unit2;
interface
procedure JakasProcedura;
implementation
procedure JakasProcedura;
begin
{ treść procedury }
end;
end.
Wybiegam w tym momencie trochę w przyszłość - funkcje i procedury będą omówione w następnych rozdziałach - ale myślę, że idea jest zrozumiała.
Sekcje initialization i finalization
W sekcji initialization umieszcza się kod, który musi być wykonany w momencie ładowania modułu do pamięci. Z kolei w sekcji finalization umieszcza się kod wykonywany w chwili usuwania modułu z pamięci. Moduł może posiadać tylko sekcję initialization, lecz nie może mieć sekcji finalization bez sekcji initialization. Obie te sekcje są opcjonalne.
Dodatkowe słowa kluczowe używane w modułach
W modułach w Pascalu występują nie tylko wspomniane wcześniej sekcje. Można w nich również stosować inne słowa kluczowe, których znaczenie wyjaśnione jest poniżej.
Słowo kluczowe const
Słowo to oznacza, że następujące po nim nazwy są identyfikatorami stałych. W module może występować dowolna liczba sekcji const, może też nie wystąpić żadna.
Stałe z założenia nie mogą zmieniać swojej wartości. Załóżmy, że w programie trzeba zapamiętać jakąś wartość, która często i wielu miejscach jest używana i która nie może być zmieniana. Można ją wtedy zadeklarować jako stałą. Przykładowo, dodajmy do kodu modułu z listingu 1.3 sekcje const. Jedną z nich dla stałych publicznych (widocznych dla innych modułów), drugą natomiast dla stałych prywatnych. Ilustruje to listing 1.4.
Listing 1.4. Moduł z sekcjami const
unit Unit2;
interface
const
AppCaption = 'Mój Program 1.0';
procedure JakasProcedura;
implementation
const
BaseX = 20;
BaseY = 200;
procedure JakasProcedura;
begin
{ kod procedury JakasProcedura }
end;
end.
Ponieważ stała AppCaption została zadeklarowana w sekcji interface, jest widoczna w całym module Unit2, a także w każdym module, który ma w swojej sekcji uses moduł Unit2. Stałe BaseX oraz BaseY są widoczne tylko w module Unit2, ponieważ zostały zadeklarowane w sekcji implementation.
Słowo kluczowe const ma oprócz tego także i inne znaczenia. Opiszę to w rozdziale „Parametry przekazywane przez stałą, wartość i referencję”.
Słowo kluczowe type
|
Słowo kluczowe type używane jest do deklarowania nowych typów danych używanych przez program. |
Najprościej wyjaśnić to na przykładzie. Powiedzmy, że Twoja aplikacja używa tablicy (grupy zmiennych połączonych razem) 20 bajtów, oraz że tablica taka będzie używana bardzo często w programie. Możesz wprowadzić nowy typ danych, który bardzo ułatwi Ci zadanie:
type
TMojaTablica = array [0..19] of Byte;
Możesz teraz używać typu MojaTablica wszędzie tam, gdzie trzeba by było mozolnie wklepywać array [0..19] of Byte.
Słowo kluczowe var
|
Słowo kluczowe var stosuje się do deklarowania zmiennych w programie. |
Zmienne można deklarować w kilku miejscach. Mogą to być zmienne dla całego modułu, mogą być zmienne lokalne procedury albo funkcji. W module może być kilka sekcji var. Listing 1.5 pokazuje przykładowy moduł z zadeklarowanymi zmiennymi.
Listing 1.5. Moduł z deklaracjami typów i zmiennych
unit Unit2;
interface
type
TMojaTablica = array [0..19] of Byte;
const
AppCaption = 'Mój Program 1.0';
var
X : Integer;
MojaTablica : TMojaTablica;
procedure JakasProcedura;
implementation
const
BaseX = 20;
BaseY = 200;
procedure JakasProcedura;
begin
{ kod procedury JakasProcedura }
end;
end.
Tak jak słowo const, var ma kilka znaczeń. Używane jest m. in. do deklarowania parametrów procedury lub funkcji jako przekazywanych przez zmienną.
|
Sekcja w module zaczynająca się słowem kluczowym var, const i type kończy się w momencie napotkania innego słowa kluczowego. |
Komentarze
Komentarze generalnie służą do opisywania kodu. Można w nich umieszczać opisy algorytmów, informacje o autorze albo po prostu notatki przeznaczone dla innych programistów czytających ten kod.
Są trzy rodzaje znaków oznaczających komentarze. Przedstawione są poniżej:
{ nie zapomnij o zwolnieniu pamięci! }
{
ADTAPI.PAS 2.50
Copyright © TurboPower Software 1996-98
}
(* Kowalski musi poprawić ten kod *)
// To jest naprawdę niezłe!
{ Nad tym trzeba jeszcze popracować }
Najczęściej stosowaną metodą jest ta widoczna w pierwszej i ostatniej linii powyższego listingu. Nawias klamrowy otwierający oznacza początek komentarza, natomiast nawias klamrowy zamykający jego koniec. Inny sposób to użycie par znaków (* i *). Ma on tę przewagę nad nawiasami klamrowymi, że można nimi obejmować duże bloki kodu zawierające już komentarze.
|
Nawiasy klamrowe mają oprócz tego inne zastosowanie w Pascalu. Użyte wraz ze znakiem $ (dolara) oznaczają dyrektywę dla kompilatora. Na przykład, żeby wyłączyć opcję kompilatora „hints”, należy w tekście programu wpisać {$HINTS OFF} Aby opcję tę z powrotem włączyć, trzeba wpisać odpowiednio {$HINTS ON}. |
Trzeci sposób oznaczania komentarzy to wstawianie na początku linii dwóch znaków „/” (slash). Przyjęło się mówić, że są to komentarze „w stylu C”, ponieważ używane są one także w C i C++. Trzeba Ci jednak wiedzieć, że nie wszystkie wersje Delphi akceptują takie komentarze. Jeżeli chcesz mieć pewność, że Twój kod zostanie zawsze prawidłowo skompilowany, musisz unikać ich stosowania.
|
Ja używam nawiasów klamrowych w kodzie, który inni będą czytać. Dwóch „slash-ów” używam tylko do szybkiego wstawiania komentarzy w jednej linii czy w dwóch w celach testowych. Par (* i *) używam bardzo rzadko. |
Komentarze są ignorowane przez kompilator. Jeżeli nie zmieniałeś domyślnych ustawień Delphi IDE, komentarze w Edytorze Kodu wyróżnione są pochyleniem i niebieskim kolorem. Ułatwia to bardzo orientację.
|
Jeżeli pracujesz w zespole, musisz prawdopodobnie czytać kod stworzony przez Twoich kolegów i na odwrót. Zwięzłe i rzeczowe komentarze pojawiające się wszędzie tam, gdzie trzeba, mogą zaoszczędzić sporo godzin pracy. Nawet gdy pracujesz sam, stosowanie komentarzy jest godną pochwały praktyką. Nie zdajesz sobie sprawy, jak trudno odczytać własny nieudokumentowany kod napisany przed miesiącem. |
Zmienne
Każda zmienna przed użyciem musi być zadeklarowana. Zmienne deklaruje się w specjalnym bloku kodu opatrzonym słowem var. Na przykład:
var
X : Integer; { deklaracja zmiennej X typu Integer }
Y : Integer; { deklaracja zmiennej X typu Integer }
Omawiałem poprzednio używanie słowa var w modułach. Potrzebne to było do deklarowania zmiennych, z których korzystały wszystkie funkcje i procedury w tym module. Oprócz tego, każda funkcja czy procedura może mieć swoje własne, lokalne zmienne, widoczne tylko wewnątrz niej. Oto przykład deklarowania zmiennych w procedurze:
procedure TForm1.Test;
var
S : String;
begin
S := 'Hello World!';
Label1.Caption := S;
end;
Po zadeklarowaniu zmiennych, możesz ich używać do przechowywania jakichś wartości (w pamięci komputera). Na poniższym przykładzie podana jest lista instrukcji manipulujących zawartością zmiennych X i Y wraz z odpowiednim opisem:
X := 100; { Zmienna 'X' posiada wartość 100 }
X := X + 50; { Zmienna 'X' posiada teraz wartość 150 }
Y := 150; { Zmienna 'Y' posiada wartość 150 }
X := X + Y; { Zmienna 'X' posiada teraz wartość 300 }
Inc(X); { Inkrementacja (zwiększenie o 1). Zmienna 'X'
posiada teraz wartość 301 }
|
Zmienna jest to miejsce w pamięci komputera zarezerwowane do przechowywania jakiejś wartości. |
Zmiennym można nadawać wartości, można je do siebie dodawać, można je inkrementować itd. W następnych rozdziałach będzie mowa o operatorach, funkcjach i procedurach do operowania na zmiennych.
Zwróć uwagę także, że każda linia kodu kończy się średnikiem. W Pascalu każda instrukcja musi być nim zakończona.
|
Ucząc się programować w Pascalu trzeba się nauczyć rozróżniać między instrukcją i wyrażeniem. Oficjalną definicją instrukcji jest wyrażenie zakończone średnikiem. Wyrażenie jest jednostką kodu, która sprowadza się do jakiejś wartości. Weźmy na przykład następującą instrukcję: c := a + b; Tekst znajdujący się po prawej stronie znaku przypisania jest wyrażeniem. Cała linia jest instrukcją. Można powiedzieć, że wyrażenie jest częścią składową instrukcji. Pojedyncza instrukcja może składać się z kilku wyrażeń. Może to na początku wydawać się niejasne, ale z czasem nabierze to dla Ciebie sensu. Na razie po prostu zapamiętaj, że na końcu każdej instrukcji musi być średnik (są od tego wyjątki, ale omówię je, kiedy je napotkamy). |
Nazwy zmiennych muszą odpowiadać regułom określonym dla identyfikatorów. Oprócz nazw zmiennych, identyfikatory mogą być nazwami funkcji, procedur, pól w rekordach, modułów i innych. Identyfikatory mogą składać się z dużych i małych liter alfabetu angielskiego, a także cyfr i znaku podkreślenia (_). Nie mogą natomiast zawierać spacji i innych znaków specjalnych. Na początku musi być litera albo znak podkreślenia. Długość identyfikatorów może być dowolna, ale tylko 255 pierwszych znaków jest znaczące. W rzeczywistości wszystkie nazwy o długości powyżej 20 znaków są niewygodne w użyciu. Poniżej przedstawione są przykłady prawidłowych nazw zmiennych:
ZmiennaOBardzoDlugiejNazwie : Integer;
moja_Zmienna : Integer;
x : Integer;
X : Integer;
Etykieta2 : String;
|
Duże i małe litery nie są w Pascalu rozróżniane. Wszystkie poniższe instrukcje są prawidłowe: var XPos : Integer;
begin ... XPos := 20; XPOS := 200; xpos := 110; XpoS := 40; ... end; Jeżeli wcześniej programowałeś w języku, w którym duże i małe litery są rozróżniane (np. C albo C++), może z początku wydawać Ci się to nienaturalne, ale szybko do tego przywykniesz. |
|
Mimo, że w Pascalu duże i małe litery nie są rozróżniane, powinieneś wypracować sobie jakąś spójną konwencję w tym temacie. Poprawi to znacznie czytelność kodu, a może też zaoszczędzić sporo pracy przy jego przenoszeniu na inną platformę. |
Typy danych w Object Pascalu
|
W Object Pascalu, typ danej informuje kompilator o sposobie przechowywania tej danej w pamięci. |
W niektórych językach programowania dozwolone jest przypisywanie jednej zmiennej wartości różnych typów, np. w BASIC-u:
X = -1;
X = 1000;
X = 3.14;
W BASIC-u interpreter troszczy się o przydzielenie odpowiedniej ilości pamięci przy każdym przypisaniu wartości tej zmiennej.
Deklarowanie zmiennych
W Object Pascalu przed użyciem zmiennej trzeba zadeklarować jej typ:
var
X1 : Integer;
X : Integer;
Y : Double;
Z : Byte;
...
X1 := -1;
X := 1000;
Y := 3.14;
Z := 27;
...
Umożliwia to kompilatorowi sprawdzanie typu zmiennej przy każdym przypisaniu. Niewłaściwe użycie zmiennej poskutkuje ostrzeżeniem albo błędem kompilatora. Dzięki temu można wyłapać i poprawić od razu tego typu błędy.
Niektóre typy danych są ze znakiem, a niektóre są bez znaku. Zmienne ze znakiem mogą przechowywać zarówno ujemne, jak i dodatnie liczby, podczas gdy zmienne bez znaku mogą zawierać jedynie liczby nieujemne. Tabela 1.1 zawiera podstawowe typy danych w Object Pascalu, ilość zajmowanej pamięci oraz zakres wartości. Tabela ta nie uwzględnia danych typu string - będą one omówione w oddzielnym rozdziale.
Tabela 1.1. Typy danych używane w Object Pascalu (w programach 32-bitowych)
Typ danej |
Rozmiar w bajtach |
Zakres wartości |
ShortInt |
1 |
-128..127 |
Byte |
1 |
0..255 |
Char |
1 |
0..255 |
WideChar |
2 |
0..65 535 |
SmallInt |
2 |
-32 768..32 767 |
Word |
2 |
0..65 535 |
LongInt |
4 |
-2 147 483 648..2 147 483 647 |
Int64 |
8 |
-9 223 372 036 854 775 808.. |
Integer |
4 |
tak samo jak LongInt |
Cardinal |
4 |
0..2 147 483 647 |
Single |
4 |
1.5x10-45..3.4x1038 |
Double |
8 |
5.0x10-324..1.7x10308 |
Real |
8 |
5.0x10-324..1.7x10308 |
Extended |
10 |
3.4x10-4932..1.1x104932 |
Comp |
8 |
-9 223 372 036 854 775 808.. |
Currency |
8 |
-922 337 203 685 477 5808.. |
Boolean |
1 |
True albo False |
Variant |
16 |
(zmienny) |
Zwróć uwagę, że typ Integer ma taki sam zakres wartości, jak typ LongInt. Dlaczego więc wprowadzono dwa różne typy danych, które właściwie niczym się nie różnią? Jest to zaszłość ze środowiska 16-bitowego, gdzie zmienna typu Integer zajmowała dwa bajty pamięci, natomiast zmienna typu LongInt cztery.
W 32-bitowym środowisku oba typy danych zajmują w pamięci 4 bajty i mają taki sam zakres wartości. Delphi generuje tylko 32-bitowe programy, więc typy Integer i LongInt są identyczne. Większość programistów preferuje raczej typ Integer.
Może zauważyłeś, że typy Int64 i Compy mają też taki sam zakres wartości. Różnica między nimi polega na różnym traktowaniu ich przez kompilator. Typ Int64 jest typem całkowitym, zaś typ Comp jest typem rzeczywistym i używany jest rzadko.
Również typy Real i Double są takie same. W poprzednich wersjach Delphi typ Real zajmował 6 bajtów w pamięci, teraz zajmuje 8. Zmiana ta została podyktowana zapewnieniem zgodności z nowymi procesorami. Typ Real uznawany jest dziś za przestarzały, zaleca się w zamian używanie typu Double.
|
Typ Int64 pojawił się wraz z Delphi 4. Zakres wartości typu Integer nie pozwala na zapamiętywanie dużych liczb całkowitych wymaganych przy obsłudze bardzo dużych dysków twardych. Na przykład Windows zawiera funkcję GetDiskFreeSpaceEx, która może zwracać wartości dużo większe niż 2 147 483 647 (maksymalna wartość zmiennej typu Integer). |
|
Typy Single, Double, Extended i Currency są typami zmiennoprzecinkowymi (liczby z miejscami po przecinku). Pozostałe typy danych są typami całkowitymi. Nie można przypisać zmiennej typu Integer wartości ułamkowej. Na przykład, poniższy kod spowoduje błąd kompilacji: var X : Integer; ... X := 3.75; Nie musisz przejmować się tym za bardzo, gdyż kompilator wyłapie i wskaże od razu tego typu błędy. Tak na marginesie, nawet nie przypuszczasz, jak rzadko w aplikacjach dla Windows używa się typów zmiennoprzecinkowych |
Konwersje między typami danych
Tam, gdzie jest to tylko możliwe i konieczne, Object Pascal przeprowadza automatyczną konwersję między różnymi typami danych. Rozpatrzmy na przykład poniższy fragment kodu:
var
Res : SmallInt;
Num1 : Integer;
Num2 : Integer;
...
Num1 := 200;
Num2 := 200;
Res := Num1 * Num2;
W tym przypadku próbuję przypisać wynik mnożenia dwóch liczb typu Integer do zmiennej typu SmallInt. Object Pascal jest w stanie przeprowadzić automatyczną konwersję. Chciałbyś wiedzieć jaki będzie rezultat tej operacji? Może się zdziwisz, ale będzie nim liczba -25 536. Pytasz, dlaczego? Spójrz do tabeli 1.1. Napisane jest tam, że typ SmallInt może mieć maksymalną wartość 32 767. Co się stanie, jeżeli zapamiętasz w zmiennej typu SmallInt wartość 32 767 i dodasz do niej 1? Otrzymasz wartość -32 768. Zasada jest taka sama, jak w 5-cyfrowym liczniku przejechanych kilometrów w samochodzie. Następnym wskazaniem po 99999 będzie 00000. Możesz sprawdzić to samemu:
Otwórz nową aplikację i umieść na formularzu etykietę oraz przycisk.
Kliknij podwójnie na przycisku, aby utworzyć dla niego procedurę obsługi zdarzenia OnClick.
Zmodyfikuj tę procedurę tak, żeby wyglądała jak następuje:
procedure TForm1.Button1Click(Sender : TObject);
var
X : SmallInt;
begin
X := 32767;
X := X+1;
Label1.Caption := IntToStr(X);
end;
Uruchom program i kliknij przycisk na formularzu.
Po kliknięciu przycisku etykieta na formularzu pokaże wartość -32 768. Funkcja IntToStr użyta w tym przykładzie zamienia liczbę typu Integer na String (tekst).
Przykład ten ilustruje zjawisko zwane przepełnieniem. Powinieneś zawsze sprawdzać, jakie maksymalne wartości może przyjmować każda zmienna i zawsze wybierać typ, który gwarantuje, że przepełnienie nie wystąpi. Nie popełnisz jednak wiele błędów, gdy standardowo będziesz stosował typ Integer, gdyż zapewnia on zakres od - 2 miliardów do 2 miliardów.
Powróćmy jednak do tematu, czyli konwersji typów. W niektórych przypadkach Delphi nie jest w stanie przeprowadzić konwersji. W takim przypadku otrzymasz komunikat o błędzie kompilacji brzmiący mniej więcej
Incompatible Types: 'Integer' and 'Real'.
Kompilator próbuje Ci w ten sposób powiedzieć, że próbujesz przypisać wartość, która nie może być przechowana w zmiennej danego typu. Inny komunikat o błędzie kompilacji, który możesz napotkać, jest wynikiem czegoś, co się nazywa sprawdzaniem zakresu (ang. range checking). Spójrz na poniższy kod:
var
X : Byte;
begin
X := 1000;
end;
Próba kompilacji takiego kodu spowoduje błąd
Constant expression violates subrange bounds.
Kompilator mówi w ten sposób, że nie może przypisać wartości 1000 zmiennej X ponieważ X jest zadeklarowana jako typ Byte. Byte zaś może przyjmować wartości z zakresu od 0 do 255.
|
Bezpieczniej jest traktować wskazówki i ostrzeżenia kompilatora jako błędy. Kompilator zwraca Ci w ten sposób uwagę, że coś jest nie tak w Twoim kodzie. Powinieneś dążyć do tego, aby Twoje programy kompilowały się bez żadnych ostrzeżeń. W rzadkich przypadkach ostrzeżeń nie można uniknąć, jednak przeanalizuj je wtedy bardzo dokładnie. |
Operatory w Object Pascalu
Operatory używane są do manipulowania danymi. Przeprowadzają one obliczenia i porównania, dokonują przypisań, oraz robią wiele różnych innych rzeczy, których prawie nigdy się nie używa. W Object Pascalu jest całe mnóstwo operatorów. Tabela 1.2 zawiera listę najczęściej używanych operatorów.
Jak widzisz, lista operatorów jest raczej długa. Nie musisz jednak wcale uczyć się ich na pamięć. Programując w Object Pascalu z czasem opanujesz je wszystkie. Niektórych operatorów będziesz używał bardzo rzadko, jeśli w ogóle, innych natomiast bardzo często. Operatorów and, or i not używa się w dwóch znaczeniach: logicznym i bitowym. Weźmy na przykład poniższy kod:
if (Wystartowano = True) and (X > 20) then
Z := X and Y;
Operator and jest tu użyty w dwóch różnych znaczeniach. Kompilator jednak wie, jak zinterpretować taki zapis. Wybiegam tu troszeczkę do przodu i może to wydawać się niejasne, lecz z czasem zaczniesz taki zapis traktować jako naturalny.
Czytając tę książkę napotkasz wiele podobnych przykładów. Zamiast uczyć się znaczenia wszystkich operatorów na pamięć staraj się po prostu zrozumieć ich działanie. Bardzo szybko nauczysz się ich stosować.
Stałe
Jak wspominałem wcześniej, stałe są to identyfikatory posiadające wartości, które się nie zmieniają. Określenia „zmienna” i „stała” nie są przypadkowe. Wartość zmiennej może być zmieniana przez programistę w dowolnym momencie. Stała natomiast, jak sama nazwa wskazuje, ma zawsze stałą wartość. Stałe są deklarowane z użyciem słowa kluczowego const. Aby zadeklarować stałą, wpisz po prostu jej nazwę i wartość, przykładowo:
const
DomyslnaSzerokosc = 400;
DomyslnaWysokosc = 200;
Opis = 'Stały napis';
Przy deklarowaniu stałej, do jej inicjalizacji używa się znaku równości, a nie znaku przypisania. Zwróć też uwagę, że nie ma podanego żadnego typu. Kompilator sam określa typ stałej na podstawie jej wartości. Stałych można używać w programie wszędzie tam, gdzie użyło by się wprost jej wartości.
Tabela 1.2. Często używane operatory
Operator |
Opis |
Przykład |
Operatory arytmetyczne |
||
+ |
Dodawanie |
x := y + z; |
- |
Odejmowanie |
x := y - z; |
* |
Mnożenie |
x := y * z; |
/ |
Dzielenie liczb rzeczywistych |
x := y / 3.14; |
div |
Dzielenie całkowite |
x := y div 10; |
Operator przypisania |
||
:= |
Przypisanie |
x := 10; |
Operatory logiczne |
||
and |
Logiczne AND |
if (x = 1) and (y = 2) then ... |
or |
Logiczne OR |
if (x = 1) or (y = 2) then ... |
Operatory relacyjne |
||
= |
Równy |
if (x = 10) then ... |
<> |
Różny od |
if (x <> 10) then ... |
< |
Mniejszy niż |
if (x < 10) then ... |
> |
Większy niż |
if (x > 10) then ... |
<= |
Mniejszy lub równy |
if (x <= 10) then ... |
>= |
Większy lub równy |
if (x >= 10) then ... |
Operatory jednoargumentowe |
||
^ |
Wskaźnik |
MojObiekt.Dane^; |
@ |
Adres |
wsk := @MojRekord; |
and |
Bitowe AND |
x := x and $02; |
or |
Bitowe OR |
x := x or $FF |
not |
Bitowe NOT |
x := x and not $02; |
not |
Logiczne NOT |
if not Prawidlowo then ... |
Inne operatory |
||
$ |
Operator kodu szesnastkowego |
x := $FF; |
[] |
Operator indeksu |
x := MojaTablica[5]; |
. |
Operator odwołania |
x := Rekord.Pole; |
Poprzez rozsądne używanie stałych można uczynić program łatwiejszym do modyfikacji, jeśli zajdzie taka potrzeba. Gdy trzeba na przykład zmienić jakąś wartość używaną w wielu miejscach w programie jako stała, wystarczy na początku pliku źródłowego zmienić deklarację tej stałej, zamiast mozolnie poszukiwać wszystkich jej wystąpień w programie.
Tablice
Wszystkie wbudowane w Object Pascal typy danych można grupować w tablice. Tablica jest po prostu zestawem wartości. Powiedzmy, że chcesz umieścić w pamięci komputera tablicę, w której chcesz zapisać 5 liczb typu Integer. Deklaracja takiej tablicy wyglądałaby następująco:
var
MojaTablica : array[0..4] of Integer;
Kompilator zaalokuje pamięć dla takiej tablicy w sposób przedstawiony na rysunku 1.4. Ponieważ każdy element tablicy zajmuje 4 bajty, cała tablica zajmuje w pamięci 20 bajtów.
Rysunek 1.4. Organizacja w pamięci tablicy 5 liczb typu Integer |
|
Po zadeklarowaniu tablicy można ją wypełnić używając operatora indeksu []:
MojaTablica[0] := -200;
MojaTablica[1] := -100;
MojaTablica[2] := 0;
MojaTablica[3] := 100;
MojaTablica[4] := 200;
Do poszczególnych elementów tablicy odwoływać się można następująco:
X := MojaTablica[3] + MojaTablica[4]; {do zmiennej X wpisana
{ zostanie wartość 300}
Tablice wielowymiarowe
Tablice mogą posiadać więcej niż jeden wymiar. Deklaracja utworzenia dwuwymiarowej tablicy liczb typu Integer wyglądałaby następująco:
var
Tablica2D : array[0..2, 0..4] of Integer;
Tablica ta zawiera 15 liczb i zajmuje w pamięci 60 bajtów. Dostęp do jej elementów jest taki sam jak w tablicy jednowymiarowej z tą różnicą, że trzeba użyć dwóch operatorów indeksu. Można to zrobić na dwa równoważne sposoby:
X := Tablica2D[1][1] + Tablica2D[2][1];
X := Tablica2D[1, 1] + Tablica2D[2, 1];
Rysunek 1.5 ilustruje rozmieszczenie w pamięci elementów tablicy dwuwymiarowej.
Rysunek 1.5.
Rozmieszczenie w pamięci elementów tablicy |
|
|
Zwykle sprawdzanie zakresu (range checking) uchroni Cię przed próbą zapisu do tablicy poza ostatnim jej elementem. Na przykład, poniższy kod nie da się skompilować: var MojaTablica : array[0..4] of Integer; X : Integer; begin
X := MojaTablica[3] + MojaTablica[5]; end; Pojawi się komunikat błędu Constant expression violates subrange bounds, ponieważ element MojaTablica[5] (szósty) nie istnieje w tablicy 5-elementowej. Zakres tablicy jest określany przy jej deklarowaniu. Na przykład, gdy chcesz utworzyć tablicę, której pierwszy element ma mieć numer 10, a ostatni 20, deklarujesz tablicę następująco: var MojaTablica : array[10..20] of Integer; Tablice stałych muszą być zadeklarowane i zainicjalizowane jednocześnie, np.: const
MojaTablica : array[0..4] of Integer = |
Funkcje Low i High
Są one często używane w operacjach na tablicach. Jak wspomniałem wcześniej, tablice mogą być deklarowane z podaniem indeksu pierwszego i ostatniego elementu. Funkcja Low zwraca indeks pierwszego elementu tablicy, a funkcja High zwraca indeks elementu ostatniego, np.:
var
X, I, Pierwszy, Ostatni : Integer;
MojaTablica : array[10..20] of Integer;
begin
...
{ wpisywanie elementów do tablicy }
...
Pierwszy := Low(MojaTablica);
Ostatni := High(MojaTablica);
X := 0;
for I := Pierwszy to Ostatni do
X := X + MojaTablica[I];
...
{ przetwarzanie X }
...
end;
W ten sposób można się zabezpieczyć przed odwołaniami do elementów tablicy o indeksach spoza zakresu.
Tablice dynamiczne
W Delphi 4 wprowadzono tablice dynamiczne. Tablica taka jest deklarowana bez podania jej rozmiaru i nie przydziela się dla niej początkowo żadnej pamięci. W czasie wykonywania programu pamięć dla tej tablicy może być przydzielona poprzez wywołanie funkcji SetLength. Oto przykładowy kod wykorzystujący tablice dynamiczne:
var
DuzaTablica : array of Integer; { Nie podano rozmiaru }
X : Integer;
begin
X := ... // tu wylicza się potrzebny rozmiar
SetLength(DuzaTablica, X);
{ Teraz można do DuzaTablica wpisywać wartości }
end;
|
Tablica dynamiczna jest to tablica, dla której pamięć przydziela się w czasie wykonania programu. Można powiększać lub zmniejszać jej rozmiar zależnie od potrzeb. |
Jest to bardzo wygodne, ponieważ umożliwia deklarowanie tablic dokładnie takich, jakie są w danych okolicznościach potrzebne. Może to dawać duże oszczędności pamięci. Załóżmy, że potrzebna jest Ci tablica liczb typu Integer. Czasami trzeba w niej zapamiętać 10 liczb, a czasami 1000. Gdyby nie było tablic dynamicznych, musiałbyś za każdym razem przydzielać pamięć na tablicę 1000-elementową nawet, gdybyś potrzebował w niej umieścić zaledwie 10.
W czasie wykonywania się programu można zmieniać wielkość tablic. Umożliwia to funkcja Copy. Powiedzmy, że na początku utworzyłeś tablicę o rozmiarze 100. Chcesz jednak powiększyć ją do 200. Powinieneś wówczas zastosować funkcję Copy jak w poniższym przykładzie:
Copy(DuzaTablica, 200);
Dotychczasowa zawartość tablicy jest zachowana z tym, że jej rozmiar zwiększył się do 200.
Można również deklarować wielowymiarowe tablice dynamiczne. Dla 2-wymiarowej tablicy kod wyglądałby następująco:
var
DuzaTablica2D : array of array of Integer;
begin
SetLength(DuzaTablica2D, 20, 20);
DuzaTablica2D[0][0] := 200;
...
end;
Po utworzeniu tablicy dynamicznej (funkcją SetLength) do jej elementów można się odwoływać tak samo, jak do elementów zwykłej tablicy.
Łańcuchy
Łańcuchy znaków (ang. strings) są bardzo często używane w programowaniu. Object Pascal posiada trzy odrębne typy łańcuchów: tradycyjne łańcuchy pascalowe (short strings), typowe dla 32-bitowych Delphi długie łańcuchy (long strings) oraz złożone z dwubajtowych znaków wide strings. Oprócz tego w Pascalu istnieją także łańcuchy z zerowym ogranicznikiem (ang. null-terminated strings). Omówię krótko te wszystkie typy, a następnie przejdę do omówienia funkcji operujących na łańcuchach.
Krótki łańcuch (Shortstring)
Typ ten jest łańcuchem znaków o stałej (w czasie wykonania programu) długości nie przekraczającej 255 znaków. Krótkie łańcuchy deklaruje się na dwa sposoby. Pierwszy to użycie słowa kluczowego ShortString. Długość takiego łańcucha będzie zawsze wynosić 255 znaków. Drugi sposób to zastosowanie operatora indeksu z podaniem długości łańcucha.
var
S1 : ShortString; { długość - 255 znaków }
S2 : string[20]; { długość - 20 znaków }
Operacje na łańcuchach typu ShortString są szybkie, ponieważ ich położenie i rozmiar w pamięci są stałe. Jednak typ ten uważany jest już za przestarzały - zaleca się używanie łańcuchów typu long strings. Pierwszy element krótkiego łańcucha zawiera zawsze bajt określający jego bieżącą długość (liczbę znaków). Tym samym długość łańcucha można określić odczytując jego pierwszy bajt. Na przykład:
var
S : ShortString; {o długości 255 znaków}
Len : Integer; {zmienna do przechowywania długości łańcucha}
begin
S := 'Hello';
Len := Ord(S[0]); {w zmiennej Len zapamiętana jest teraz długość
{łańcucha, czyli 5}
end;
Do określenia długości łańcucha można użyć także funkcji Length. Objaśnię to szerzej już wkrótce.
|
Funkcja Ord zamienia wartość typu Char na Integer. Jest ona także używana przy typach wyliczeniowych. |
Gdy zachodzi taka konieczność, można także wpisywać wartości do pierwszego bajtu krótkiego łańcucha. Jest to jednak sztuczka stosowana przez zaawansowanych programistów.
Długi łańcuch
Długi łańcuch jest typem łańcuchowym, którego długość jest określana dynamicznie i graniczona jedynie dostępną pamięcią. Przydzielanie i zwalnianie pamięci dla długich łańcuchów odbywa się w miarę potrzeb. Są one przez to bardziej elastyczne, jednak kosztem mniejszej szybkości, zwłaszcza gdy wykonywanych jest na nich wiele operacji. We wszystkich tych przypadkach, gdy szybkość działania programu nie jest decydująca, powinieneś generalnie stosować właśnie długie łańcuchy.
Aby zadeklarować zmienną typu long string, stosuje się po prostu słowo kluczowe string bez żadnych parametrów:
var
S : string; {long string, alokowany dynamicznie}
Długi łańcuch można modyfikować praktycznie bez żadnych ograniczeń, nie przejmując się jego długością itp. Przydział pamięci odbywa się całkowicie automatycznie. Używanie przez to tego typu jest bardzo łatwe.
W odróżnieniu od łańcucha shortstring długi łańcuch nie posiada zerowego elementu, tak więc jego długość odczytać można jedynie za pomocą funkcji Length. Przydzielenie łańcuchowi wystarczającej pamięci odbywa się automatycznie przy przypisaniu mu wartości, choć może być wykonane również za pomocą procedury SetLength (co konieczne jest wtedy, gdy chcemy operować na jego poszczególnych elementach - na sposób tablicowy - przyp. red.)
Łańcuchy WideString
Typ ten używany jest w połączeniu z funkcjami Windows API operującymi na znakach 2-bajtowych (Unicode). Podobnie jak dla długie łańcuchy, łańcuchy Widestring mają długość ograniczoną jedynie dostępną pamięcią i są także alokowane dynamicznie. Nie będę się nad nimi rozwodził, ponieważ ich użycie ogranicza się praktycznie do stosowania w funkcjach OLE.
Łańcuchy z zerowym ogranicznikiem: PChar i Array of Char
W odróżnieniu od Object Pascala, języki C i C++ nie posiadają właściwie prawdziwych typów łańcuchowych. W tych językach łańcuchy zaimplementowane są jako ciągi znaków zakończone znakiem NULL (tj. znakiem o kodzie zero). Tablice znaków nie mają bajtu określającego długość, więc do określenia końca ciągu używany jest znak NULL. Ponieważ Windows było napisane w C, wiele funkcji Windows API wymaga tablic znaków jako parametrów. Ponieważ Pascalowe łańcuchy nie są tablicami znaków, trzeba było znaleźć jakiś sposób, aby umożliwić wykorzystywanie w Pascalu funkcji Windows API. Typ PChar spełnia tę funkcję. Może być używany wszędzie tam, gdzie wymagane są tablice znaków. Przykładem może być funkcja Windows MessageBox. Funkcja ta, która wyświetla na ekranie standardowe okienko informacyjne, posiada następującą deklarację:
function MessageBox(hWnd: HWND; lpText, lpCaption: PChar;
uType: UINT) : Integer;
Drugi i trzeci parametr jest wskaźnikiem na tablicę znaków (drugi na tekst umieszczany w okienku, trzeci na tytuł okienka). Aby wywołać tę funkcję z poziomu programu Delphi, trzeba użyć typu PChar następująco:
var
Tekst : string;
Tytul : string;
begin
Tekst := 'To jest test.';
Tytul := 'Komunikat testowy.';
MessageBox(0, PChar(Tekst), PChar(Tytul), 0);
end;
Powyższy przykład pokazuje zastosowanie typu PChar do konwersji długiego łańcucha do łańcucha z zerowym ogranicznikiem. Można też typu PChar używać wprost:
var
Tekst : PChar;
begin
Tekst := 'To jest test.';
MessageBox(0, Tekst, 'Uwaga', 0);
end;
Ponieważ cała siła typów łańcuchowych w Pascalu leży w łatwości operowania nimi, prawdopodobnie nie będziesz często używał typu PChar . Prawdopodobnie jedynym jego zastosowaniem będzie konwersja długiego łańcucha na łańcuch typu null-terminated. Zwróć uwagę, że tam gdzie funkcja Windows spodziewa się parametru typu PChar, można zastosować wprost tekst objęty apostrofami.
Można ostatecznie użyć zamiast typu PChar tablicy znaków:
var
Tekst : array[0..20] of Char;
begin
Tekst := 'To jest test.';
MessageBox(0, Tekst, 'Uwaga', 0);
end;
Nie ma właściwie znaczenia, której metody z przedstawionych powyżej użyjesz. Pamiętaj tylko o tym, że nie możesz zastosować typu shortstring (pochodzącego z Pascala) jako parametru wywołania funkcji Windows API, który to parametr musi być łańcuchem z zerowym ogranicznikiem. W tych przypadkach musisz użyć typu PChar lub tablicy znaków.
Operacje na łańcuchach
Typy łańcuchowe w Pascalu mają kilka wspólnych elementów. Poniższe sekcje opisują ogólne operacje na łańcuchach wspólne dla wszystkich ich typów.
Łączenie łańcuchów z użyciem operatora +
Często spotykanym zadaniem jest konkatenacja (złączenie, dodanie do siebie) dwóch lub więcej łańcuchów. Można to łatwo zrobić używając operatora +, na przykład:
var
S1 : string;
S2 : string;
begin
S1 := 'To jest';
S2 := 'test.';
Label1.Caption := S1 + ' ' + S2;
end;
Kod ten dodaje do siebie 3 łańcuchy (zmienną S1, spację i zmienną S2) i umieszcza wynik we właściwości Caption etykiety. Jako argument operatora + może występować każde wyrażenie lub funkcja, która w wyniku daje łańcuch, tak jak ilustruje to kolejny przykład:
var
X : Integer;
begin
X := 199;
Label1.Caption := 'Wynik: ' + IntToStr(X);
end;
Operator indeksu
Innym często używanym operatorem w działaniach na łańcuchach jest operator indeksu []. Stosując go można odwołać się do pojedynczego znaku łańcucha:
var
S1 : string;
S2 : Char;
begin
S1 := 'Hello World!';
S2 := S1[1];
Label1.Caption := S2;
end;
W tym przykładzie zmienna S2 jest typu Char, ale równie dobrze mogła być to zmienna typu shortstring, widestring lub długi łańcuch. Object Pascal automatycznie przeprowadziłby konwersję. Na poziomie aplikacji nie musisz martwić się o kompatybilność różnych typów łańcuchowych. Operator indeksu jest użyteczny w sytuacjach, kiedy musisz przeszukiwać łańcuchy znak po znaku.
Pierwszy znak łańcucha ma indeks 1. Pamiętaj, że zerowy element zmiennej shortstring zawiera bieżącą długość łańcucha. Długie łańcuchy i łańcuchy widestring nie posiadają zerowego elementu.
Znaki sterujące w łańcuchach
Object Pascal umożliwia umieszczanie w łańcuchach znaków sterujących. Znaków sterujących nie widać na ekranie i drukarce, posiadają one kody ASCII od 0 do 31. Są to m. in. znak nowego wiersza, znak dzwonka, znaki sterujące łączem szeregowym itp.
Znaki sterujące wstawia się do łańcuchów przy użyciu znaku #. Jeżeli, przykładowo, chcesz wpisać do łańcucha znak ESC (ASCII 27), musisz zrobić to w następujący sposób:
S := 'To jest test. Następny znak - ESCAPE.'#27' Koniec.';
Zauważ, że znak #27 umieszczony jest poza tekstem w apostrofach, oraz że nie ma spacji między znakiem #27 a sąsiadującymi stałymi tekstowymi. Oczywiście można też używać znaków sterujących w połączeniu ze zmiennymi:
S1 := 'o jest test. Następny znak - ESCAPE.';
S2 := ' Koniec.';
S3 := S1 + #27 + S2;
Możesz od razu sprawdzić to w praktyce. Umieść na formularzu przycisk i etykietę. Podwójnie kliknij na przycisku i w treści procedury OnClick umieść następującą linię:
Label1.Caption := 'Linia 1' + #10 + 'Linia 2';
Teraz uruchom program i kliknij przycisk. Etykieta zawierać będzie dwie linie tekstu, tak jak pokazano na rysunku 1.6.
Rysunek 1.6. Etykieta z dwiema liniami tekstu |
|
#10 jest znakiem nowego wiersza, czyli włączenie go do łańcucha powoduje rozbicie tego ostatniego na dwie linie.
Dzielenie tekstu na kilka linii kodu
Czasami trzeba rozbić długi tekst wprowadzany w kodzie na kilka linii, żeby zwiększyć jego czytelność. Zdarza się czasami, że trzeba wpisać tekst o długości ponad 200 znaków. Mógłbyś umieścić cały ten tekst w jednej linii kodu (maksymalna długość linii kodu w Delphi wynosi 1024 znaki), jednak uczyniłoby to kod prawie niemożliwym do odczytania. Można długie teksty rozbijać na kilka linii. Służy do tego operator +. Przykład przedstawiony jest poniżej:
MessageBox(0, 'To jest bardzo, bardzo długi tekst, który ' +
'zdaje się nie mieć końca. Aby uczynić go bardziej ' +
'czytelnym, można go umieścić w kilku ' +
'liniach.', 'Uwaga', 0);
Pamiętasz, co mówiłem wcześniej o średniku na końcu każdej instrukcji? To jest właśnie przykład instrukcji rozciągniętej w kilku liniach. W pojęciu kompilatora to wszystko jest jedną instrukcją, więc średnik umieszczony jest tylko w jednym miejscu, a nie na końcu każdej linii.
Porównywanie łańcuchów
Łańcuchy można porównywać między sobą z użyciem operatorów porównania. Tabela 1.3 przedstawia listę najczęściej używanych operatorów i ich opis.
Tabela 1.3. Operatory porównywania łańcuchów
Operator |
Opis |
= |
Równy |
<> |
Nie równy |
< |
Mniejszy niż |
> |
Większy niż |
<= |
Mniejszy lub równy |
>= |
Większy lub równy |
Operatory te porównują łańcuchy opierając się na ich kodach ASCII. Najczęściej prawdopodobnie będziesz używał operatorów równości żeby zbadać, czy dany łańcuch jest równy innemu lub od niego lub różny. Jeżeli będziesz sortował łańcuchy, użyjesz także prawdopodobnie innych operatorów. W poniższym przykładzie następuje sprawdzenie, czy dany łańcuch zawiera określony ciąg znaków:
if NazwaPliku = 'TEST.TXT' then
OpenFile(NazwaPliku)
else
Blad;
Funkcje operujące na łańcuchach
Object Pascal posiada wiele funkcji i procedur operujących na łańcuchach. Tabela 1.4 przedstawia te najczęściej używane. Kompletna lista funkcji i procedur operujących na łańcuchach zawarta jest w systemie pomocy Delphi.
Tabela 1.4. Funkcje i procedury operujące na łańcuchach
Nazwa |
Opis |
Copy |
Zwraca podciąg łańcucha znaków |
Delete |
Usuwa część łańcucha |
Format |
Zwraca sformatowany łańcuch na podstawie ciągu formatującego i rzekazanych parametrów |
Insert |
Wstawia łańcuch do innego łańcucha |
IntToStr |
Zamienia liczbę typu Integer na łańcuch |
Length |
Podaje długość łańcucha |
LowerCase |
Zamienia litery w łańcuchu na małe |
Pos |
Podaje pozycję podciągu znaków w łańcuchu |
StringOfChar |
Zwraca łańcuch wypełniony podaną liczbą określonego znaku |
StrPas |
Zamienia łańcuch z zerowym ogranicznikiem na łańcuch pascalowski |
StrPCopy |
Zamienia łańcuch pascalowski na łańcuch z zerowym ogranicznikiem |
StrToInt |
Zamienia łańcuch na liczbę typu Integer. |
StrToIntDef |
Zamienia łańcuch na liczbę typu Integer. Jeżeli konwersja jest niemożliwa, funkcja podstawia wartość domyślną i nie generuje wyjątku |
StrToXXX |
Zestaw funkcji zamieniających łańcuch na liczby zmiennoprzecinkowe, Currency, Date, albo Time |
Trim |
Obcina początkowe i końcowe spacje łańcucha |
UpperCase |
Zamienia litery w łańcuchu na duże |
XXXToStr |
Zestaw funkcji zamieniających liczby zmiennoprzecinkowe, Currency, Date albo Time na łańcuch |
|
Object Pascal posiada cały szereg funkcji operujących na łańcuchach z zerowym ogranicznikiem. Nie omawiam ich, gdyż w przeważającej większości przypadków będziesz mieć do czynienia z łańcuchami pascalowskimi. Jeżeli będziesz potrzebować informacji na temat łańcuchów z zerowym ogranicznikiem, możesz je znaleźć w systemie pomocy. |
Kilka funkcji z tabeli 1.4 wartych jest szerszego omówienia. Funkcja StrToInt konwertuje łańcuchy na liczby typu Integer. Załóżmy, że na formularzu umieściłeś pole edycji służące to pobierania od użytkownika liczb typu Integer. Ponieważ pole edycji może jedynie przechowywać tekst, musisz przekonwertować ten tekst na Integer. Możesz to zrobić następująco:
Value := StrToInt(Edit1.Text);
Inne funkcje StrToXXX (StrToFloat, StrToDate, itd.) działają tak samo. Funkcje te generują wyjątek, jeżeli konwersja taka nie może być przeprowadzona (gdy użytkownik wpisze na przykład S123 - litera S nie może być przekształcona na liczbę).
Funkcja Format umożliwia zbudowanie łańcucha przy użyciu ciągu formatującego i dodatkowych parametrów. W poniższym przykładzie dodane są do siebie dwie liczby, a ańcuch pokazujący wynik tworzony jest poprzez wywołanie funkcji Format:
var
S : string;
X : Integer;
begin
X := 10 * 20;
S := Format('Wynik: %d', [X]);
Label1.Caption := S;
end;
Po wykonaniu się tego kodu, etykieta zawiera następujący tekst:
Wynik: 200
W tym przykładzie, sekwencja %d znaczy „w tym miejscu będzie wyświetlona liczba typu Integer”. Na końcu ciągu formatującego wstawiona jest zmienna X, której wartość ma być wyświetlona w tym miejscu.
Funkcja Format ma zmienną liczbę argumentów (to właśnie dlatego w powyższym przykładzie X jest w nawiasach kwadratowych - jest to tablica stałych). Ciąg formatujący jest wymagany, natomiast liczba parametrów umieszczonych za ciągiem formatującym może się zmieniać. Poniżej przedstawiony jest przykład wywołania funkcji Format z życiem trzech dodatkowych parametrów:
var
X : Integer;
Y : Integer;
begin
X := 20;
Y := 5;
Label1.Caption := Format('%d + %d = %d', [X, Y, X + Y]);
end;
W rezultacie na ekranie pojawia się
20 + 5 = 25
Zwróć uwagę, że przypisuję tu wartość zwróconą przez funkcję Format wprost właściwości Caption etykiety. W poprzednim przykładzie przypisałem wynik funkcji Format do zmiennej, choć nie było to właściwie konieczne.
Inne znaki formatujące używane są do wyświetlania liczb zmiennoprzecinkowych, w otacji naukowej, w kodzie szesnastkowym, lub do wyświetlania znaków i łańcuchów. Przy wyświetlaniu liczb zmiennoprzecinkowych można określić liczbę znaków po przecinku, lub liczbę cyfr liczb całkowitych. Szczegółowy opis wszystkich znaków formatujących znaleźć można w systemie pomocy pod hasłem „Format Strings”.
Podsumowanie
W tym rozdziale poznałeś dużo nowych rzeczy. Najpierw zapoznałeś się z Delphi IDE tworząc programy Hello World i Hello World II. Potem nastąpiła trochę poważniejsza część, czyli poznawanie podstaw Object Pascala. Przerobiliśmy razem całkiem sporo materiału. Nie przejmuj się, jeżeli nie zapamiętałeś wszystkiego od razu. Zawsze będzie można tu zajrzeć w razie potrzeby.
Warsztat
Warsztat składa się z pytań kontrolnych oraz ćwiczeń utrwalających i pogłębiających zdobytą wiedzę. W razie trudności lub wątpliwości, odpowiedzi do tych pytań zamieszczone są w Dodatku A, „Quiz - odpowiedzi”.
Pytania i odpowiedzi
Jaka jest różnica pomiędzy aplikacją GUI Win32 i aplikacją terminalową Win32?
Aplikacje GUI są to aplikacje z graficznym interfejsem użytkownika. Posiadają one okna z paskiem tytułu, menu i obszarem roboczym. Z kolei aplikacje terminalowe uruchamia się z poziomu okna trybu MS-DOS. Aplikacje terminalowe mają interfejs taki sam, jak programy DOS-owe.
Czy mogę po zadeklarowaniu stałej zmienić później jej wartość?
Nie. Stałe - jak sama nazwa wskazuje - są stałe. Jeżeli chcesz zmieniać jej wartość, użyj zamiast tego zmiennej.
Czy moduły muszą posiadać sekcję interface?
Tak (to znaczy wszystkie moduły poza głównym plikiem źródłowym projektu). Sekcja interface może być pusta, ale to słowo kluczowe musi się znaleźć w module.
Którego typu powinienem raczej używać w moich aplikacjach Delphi: shortstring czy długich łańcuchów?
Zaleca się raczej używanie długich łańcuchów. Nie posiadają one praktycznie limitu długości, poza tym przydzielanie dla nich i zwalnianie pamięci odbywa się całkowicie automatycznie. Łańcuchy shortstring są nieco szybsze, jednak prawie zawsze różnica szybkości działania jest niezauważalna.
Czy mogę wpisać do zmiennej typu Integer liczbę z miejscami po przecinku?
Nie. Nie można zmiennym typu całkowitego przypisywać liczb zmiennoprzecinkowych.
Czy Object Pascal posiada zabezpieczenia chroniące przed skutkami zapisywania do pamięci poza końcem tablicy?
W przeważającej większości przypadków - tak. Sprawdzanie zakresu (range checking) wyłapie wszystkie próby indeksowania tablic poza ich obszarem.
Quiz
Jakie jest rozszerzenie pliku zawierającego moduł Object Pascala?
Jak brzmi słowo kluczowe określające sekcję deklaracji zmiennych?
Co robi funkcja IntToStr?
W jakim celu używa się w modułach listy uses?
Czy dla kompilatora dwie poniższe deklaracje różnią się od siebie? Dlaczego?
var
top : Integer;
Top : Integer;
Jak łączy się łańcuchy w Pascalu?
Jak można wstawić znak sterujący do łańcucha?
Jaka jest maksymalna długość łańcucha shortstring?
Ile bajtów może pomieścić tablica zadeklarowana następująco:
MojaTablica : array[0..10] of Byte;
Jaki jest numer pierwszego elementu w tablicy: 0 czy 1?
Ćwiczenia
Napisz program, który po uruchomieniu się wyświetli w oknie napis „Witaj w Delphi!”.
Zmodyfikuj program z ćwiczenia 1 tak, aby napis brzmiał: „Hej tam!” (wskazówka: musisz zmienić jedynie właściwość Caption komponentu Label).
Napisz program posiadający dwie zmienne. Przypisz wartości tym zmiennym. Pomnóż je i wypisz wynik na ekranie.
Napisz program, który przypisuje tekst „W tuzinie jest jaj” pewnej zmiennej, a później wstawia ciąg „12” w odpowiednie miejsce tego łańcucha. Pokaż wynik w postaci etykiety.
Napisz program, który buduje łańcuch z ćwiczenia 4 przy użyciu funkcji Format.
Zarówno długie łańcuchy, jak i łańcuchy Widestring posiadają zerowy ogranicznik - wyjaśnia to, dlaczego długi łańcuch może być traktowany jako tablica znaków zakończona znakiem NULL (przyp. red.)
24
23
62 Część I
Rozdzia³ 1. ♦ Zaczynamy 61
62 C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\01.doc
C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\01.doc 61