R02-04.DOC

(639 KB) Pobierz
Szablon dla tlumaczy

 

 

 

 

Rozdział 2.

Język Object Pascal

Pozostawmy na chwilę wizualne aspekty Delphi i przyjrzyjmy się bliżej sta­nowiącemu podstawę Delphi językowi Object Pascal. Ponieważ niniejsza książka przeznaczona jest raczej dla zaawansowanych czytelników, z jednej strony ograniczyliśmy się jedynie do zestawienia najważniejszych cech tego języka, z drugiej natomiast wprowadziliśmy pewne porównania jego elementów z innymi językami wysokiego poziomu — jak C++, Visual Basic i Java — przy założeniu, że Czytelnik posiada o nich podstawową wiedzę. Obecna wersja Object Pascala różni się znacznie od tej z Delphi 1 czy Delphi 2, zalecamy więc uważne przestudiowanie treści niniejszego rozdziału — nie da się bowiem w pełni wykorzystać Delphi bez dogłębnej znajomości języka, na bazie którego zostało zbudowane.

 

Notatka

Ilekroć wspominamy w niniejszym rozdziale o języku C, mamy na myśli elementy wspólne dla C i C++. Gdy mowa jest o elementach specyficznych dla C++ zaznaczamy to wyraźnie.

Komentarze

Komentarz jest najprostszym elementem języka programowania — stanowi swobodny tekst mający znaczenie jedynie dla czytelności programu; przez kompilator jest on cał­kowicie ignorowany. Object Pascal dopuszcza trzy rodzaje ograniczników komentarza:

·         nawiasy klamrowe { .. }, znane z Turbo Pascala,

·         ograniczniki typu „nawias-gwiazdka” (* ... *), również występujące w Turbo Pascalu,

·         podwójny ukośnik // (ang. double slash), zapożyczony z języka C++.

Oto przykłady poprawnych komentarzy w Object Pascalu:

     to jest komentarz języka Object Pascal,

     podstawy Delphi 6

}

 

 

(* to również jest komentarz,

   tylko z innymi ogranicznikami

*)

 

 

//Ten komentarz musi zmieścić się w jednej linii

 

 

// Ten komentarz został, dla odmiany,

// podzielony pomiędzy kilka linii,

// z których każda traktowana jest przez kompilator jako

// niezależny komentarz,

// choć przecież dla użytkownika nie ma to żadnego znaczenia.

Za koniec komentarza rozpoczynającego się od podwójnego ukośnika przyjmuje się koniec linii.

 

Ostrzeżenie

Komentarze tej samej postaci nie mogą być zagnieżdżane, gdyż jest to sprzecz­ne z regułami składniowymi języka. Na przykład w poniższym przykładzie

{   { próba zagnieżdżenia komentarza } }

początkiem komentarza jest oczywiście pierwszy z nawiasów otwierających, lecz końcem — pierwszy, nie drugi nawias zamykający. Nie ma jednak żadnych prze­szkód, aby zagnieżdżać komentarze różnych typów, na przykład:

(* Poniższy komentarz {nadmiar} wskazuje błędną
   instrukcję *)

 

{  poniższe komentarze w formie (*  *) ograniczają
   tekst do usunięcia

}

W komentarzu rozpoczynającym się od podwójnego ukośnika znaki { } (* *) mogą oczywiście występować bez żadnych ograniczeń, gdyż końcem komentarza nie jest żaden wyróżniony znak, lecz koniec linii.

 

Nowości w zakresie procedur i funkcji

Ponieważ procedury i funkcje stanowią najbardziej uniwersalny element wszystkich języ­ków algorytmicznych, zrezygnujemy w tym miejscu z ich wyczerpującego opisu (który Czytelnik znaleźć może m.in. w dokumentacji Delphi), koncentrując się na tych ich cechach, które odróżniają Object Pascal od Turbo Pascala oraz tych, które pojawiły się w późniejszych wersjach Delphi (począwszy od Delphi 3).

 

Puste nagłówki wywołań

Wyjątkowo, opisywana w tym miejscu konstrukcja obecna jest w Object Pascalu już od Delphi 2, mimo to jest jednak na tyle mało popularna, iż zasługuje przynajmniej na krótką wzmiankę. Otóż, wywołując funkcję lub procedurę nie posiadającą parametrów, możemy użyć pary nawiasów na wzór języka C lub Java, na przykład:

Form1.Show();

...

R := CurrentDispersion();

co oczywiście jest równoważne

Form1.Show;

...

R := CurrentDispersion;

Nie jest to może żadna rewelacja językowa, lecz z pewnością drobne ułatwienie życia programistom, którzy oprócz Object Pascala używają również języków wcześniej wymienionych, w których użycie wspomnianych nawiasów jest obowiązkowe.

 

Przeciążanie

W Delphi 4 wprowadzono mechanizm przeciążania (overloading) pro­cedur i funkcji, umożliwiający zdefiniowanie całej rodziny procedur (funkcji), posiadających tę samą nazwę, lecz różniących się postacią listy parametrów wywołania, na przykład:

function Divide(X, Y: Real): Real; overload;

begin

  Result := X/Y;

end;

 

function Divide(X, Y: Integer): Integer; overload;

begin

  Result := X div Y;

end;

Informacją o przeciążeniu procedury funkcji jest klauzula overload (jak w powyższym przykładzie).

Kompilator, analizując postać wywołania przeciążonej procedury (funkcji), automatycznie wybierze właściwy jej eg­zemplarz (zwany często jej aspektem), aby jednak zadanie to było wykonalne, poszczególne aspekty faktycznie muszą różnić się od siebie. Nie jest tak chociażby w poniższym przykładzie:

procedure Cap(S: string); overload;

...

 

procedure Cap(var Str: string); overload;

...

Wywołanie

 

Cap(S);

 

pasuje bowiem do obydwu aspektów — kompilator uzna drugi z nich za próbę redefinicji pierwszego i zasygnalizuje błąd.

Przeciążanie procedur i funkcji jest chyba najbardziej oczekiwaną nowością Object Pascala od czasu Delphi 1 i, jakkolwiek bardzo pożądane i użyteczne, stanowi jedno z odstępstw od  rygorystycznej kontroli typów danych (tak charakterystycznej dla początków Pascala). Mamy wszak do czynienia z różnymi procedurami (funkcjami), kryjącymi się pod tą samą nazwą — przypomina to identycznie nazwane procedury (funkcje) rezydujące w różnych modułach. Należy więc korzystać z przeciążania rozsądnie, a w żadnym wypadku nie należy go nadużywać.

Zagadnieniem podobnym do przeciążania procedur i funkcji jest przeciążanie metod, którym zajmiemy się w dalszej części niniejszego rozdziału.

Domyślne parametry

To kolejna nowość wprowadzona w Delphi 4, umożliwiająca uproszczenie wywołań procedur i funkcji poprzez pominięcie jednego lub więcej końcowych parametrów listy. W treści proce­dury (funkcji) parametry te posiadać będą wartości domyślne, ustalone w jej definicji. Oto przykład deklaracji procedury z jednym parametrem domyślnym:

procedure HasDefVal( S: String; I: Integer = 0);

Ponieważ drugiemu z parametrów definicja przyporządkowuje domyślną wartość 0, więc wywołanie

HasDefVal('Hello');

równoważne jest wywołaniu

HasDefVal('Hello', 0);

Parametry z wartościami domyślnymi muszą wystąpić w końcowej części listy — nie mogą być „przemieszane” z pozostałymi parametrami, tak więc poniższa deklaracja

Procedure NoProperDefs( X: Integer = 1; Y : Real);

jest błędna, poprawna jest natomiast deklaracja

Procedure ProperDefs( X: Integer = 1; Y : Real = 0.0);

 

Parametry z deklarowaną wartością domyślną nie mogą być przekazywane przez referencję (var), lecz jedynie przez wartość lub przez stałą (const); wiąże się to z oczywistym wymogiem, aby parametr aktualny wywołania był wyrażeniem o dającej się ustalić wartości. Wymóg ten narzuca również ograniczenie na typ parametru, któremu przypi­suje się wartość domyślną — nie mogą w tej roli wystąpić rekordy, zmienne warianto­we, pliki, tablice i obiekty. Ograniczeniu podlega także sama wartość domyślna przypisywana parametrowi — może ona być wyrażeniem typu porządkowego, wskaźnikowego lub zbiorowego.

Pewnego komentarza wymaga użycie parametrów domyślnych w połączeniu z przecią­żaniem procedur i funkcji. Należy uważać, aby nie uniemożliwić kompila­torowi jednoznacznego zidentyfikowania właściwego aspektu procedury (funkcji) przeciążanej — rozróżnienie takie nie jest możliwe chociażby w poniższym przykładzie:

procedure Confused(I: Integer); overload;

...

 

procedure Confused(I: Integer; J: Integer = 0); overload;

...

 

 

var

  X: Integer;

begin

...

Confused(X);    //  Ta instrukcja spowoduje błąd kompilacji

Mechanizm parametrów domyślnych oddaje nieocenione usługi przy unowocześnianiu istniejącego oprogramowania. Załóżmy na przykład, iż następującą procedurę

Procedure MyMessage( Msg:String);

wyświetlającą komunikat w środkowej linii okna, chcielibyśmy wzbogacić w możliwość jawnego wskazania linii (poprzez drugi parametr). Nawet jeżeli przyjmiemy, iż podanie 0 jako numeru linii oznaczać będzie tradycyjne wyświetlanie w środkowej linii, to i tak nie zwolni nas to z modyfikacji wszystkich wywołań procedury MyMessage() w kodzie programu. Ściślej — byłoby tak, gdyby nie mechanizm domyślnych parametrów, bowiem począwszy od Delphi 4 możemy dopuścić wywołanie procedury MyMessage() z jednym parametrem, przyjmując w takiej sytuacji, iż opuszczony, domyślny drugi parametr ma wartość 0:

Procedure MyMessage ( Msg : String; Line: byte = 0 );

Wówczas wywołanie

MyMessage('Hello', 1)

spowoduje wyświetlenie komunikatu w pierwszej linii okna, natomiast efektem wywołania

MyMessage('Hello')

będzie wyświetlenie komunikatu w linii środkowej. I nie trzeba przy tym niczego zmieniać, poza oczywiście samą procedurą MyMessage().

Zmienne

W języku C i w Javie możliwe jest deklarowanie zmiennych dopiero w momencie, gdy faktycznie okazują się potrzebne, na przykład:

 

void foo(void)

{

  int x = 1;

  x++;

  int y = 2;

  float f;

//  ... i tak dalej

}

Natomiast w języku Pascal wszystkie deklaracje zmiennych muszą być zlokalizowane przed blo­kiem kodu danej procedury, funkcji lub programu głównego:

Procedure Foo;

var

  x, y : integer;

  f : double;

begin

  x := 1;

  Inc(x);

  y := 2;

  (*

    itd.

  *)

end;

Może się to wydawać nieco krępujące, lecz w rzeczywistości prowadzi do programu bardziej czytelnego i mniej podatnego na błędy — co jest charakterystyczne dla Pascala, stawiającego raczej na bezpieczeństwo aplikacji niż hołdowanie określonym konwencjom.

 

Notatka

Object Pascal i Visual Basic, w przeciwieństwie do Javy i C, niewrażliwe są na wielkość liter w nazwach elementów składniowych. Wrażliwość taka zwiększa co prawda elastyczność języka, lecz niepomiernie zwiększa również prawdopodobieństwo popeł­nienia trudnych do wykrycia błędów. W Pascalu raczej trudno odczuć brak takiej elastyczności, za to programista ma dość dużą swobodę stylistyczną w pisow­ni nazw, na przykład nazwa

prostesortowaniemetodąprzesiewaniazograniczeniami

staje się bardziej czytelna, gdy jest zapisana w tzw. notacji „wielbłądziej”

ProsteSortowanieMetodąPrzesiewaniaZOgraniczeniami

 

Deklaracje zmiennych tego samego typu mogą być łączone, na przykład deklarację

var

  Zmienna1 : integer;

  Zmienna2 : integer;

można skrócić do postaci

var

  Zmienna1, Zmienna2 : integer;

Jak widać, po liście zmiennych następuje dwukropek i nazwa typu.

Nadawanie zmiennym wartości odbywa się w innym miejscu niż ich deklarowanie, mia­nowicie w treści programu. Nowością, która pojawiła się w Delphi 2 jest możliwość ini­cjowania zmiennych już podczas ich deklaracji, na przykład:

var

  i : integer = 10;

  P : Pointer = NIL;

  s : String = 'Napis domyślny';

  d : Double = 3.1415926 ;

Jest to jednak dopuszczalne wyłącznie dla zmiennych globalnych; kompilator nie zezwoli na inicjowanie w ten sposób zmiennych lokalnych w procedurach i funkcjach.

 

Wskazówka

Kompilator dokonuje również automatycznej inicjalizacji wszystkich zmiennych globalnych bez deklarowanej wartości początkowej, zerując zajmowaną przez nie pamięć; tak więc wszystkie zmienne całkowitoliczbowe otrzymują wartość 0, zmienno­przecinkowe — wartość 0.0, łańcuchy stają się łańcuchami pustymi, wskaźniki otrzymują wartość NIL itp.

Stałe

Stałe (ang. constants) są synonimami konkretnych wartości występujących w programie. Deklaracja stałych poprzedzona jest słowem kluczowym const i składa się z jednego lub więcej przypisań wartości nazwom synonimicznym, na przykład:

const

  DniWTygodniu = 7;

  Stanowisk = 7;

  TaboretyNaStanowisku = 4;

  TaboretyOgolem = TaboretyNaStanowisku * Stanowisk;

  Komunikat = 'Przerwa śniadaniowa';

 

Zasadniczą różnicą między Object Pascalem a językiem C — w zakresie deklaracji stałych — jest to, że Pascal nie wymaga deklarowania typów stałych; kompilator sam ustala typ stałej na podstawie przypisywanej wartości. Ponadto, stałe synonimiczne typów skalar­nych nie zajmują dodatkowego miejsca w pamięci programu, gdyż istnieją jedynie w czasie jego kompilacji. Więc na przykład następujące deklaracje języka C:

const float AdecimalNumber = 3.14

const int i = 10

const char *ErrorString = "Uwaga, niebezpieczeństwo";

posiadają następujące odpowiedniki w Pascalu:

 

const

  AdecimalNumber = 3.14;

  i = 10;

  ErrorString = 'Uwaga, niebezpieczeństwo';

 

Kompilator, ustalając typ stałej, wybiera typy o możliwie najmniejszej liczebności. Typ stałej całkowitoliczbowej ustalany jest w następujący sposób:

Tabela 2.1

Tej tabeli nie ma w oryginale

Ustalane przez kompilator typy stałych całkowitoliczbowych

 

Wartość

Typ

od –2^63 do –2.147.483.649

Int64

od –2.147.483.648...

Zgłoś jeśli naruszono regulamin