Wstęp

Wprowadzenie

Skrypt powstał na podstawie strony Nowiesza [tutaj] (to pochylone to skopiowane), Żółtej Księgi (wszelkie cytaty z niej zostały tutaj umieszczony wyłącznie dla celów informacyjnych :P) oraz własnych przemyśleń.

Uwagi

Słać tutaj: malinowskirafal(at)wp(dot)pl

Zadania i odpowiedzi

Co to jest projekt i jakie zbiory są jego elementami?

Projekt to program lub biblioteka łączona dynamicznie. Składa się z:

.DPR
zbiór programowy, przy czym dla każdego projektu istnieje tylko jeden taki zbiór, po skompilowaniu projektu plik .EXE lub .DLL będzie miał taką samą nazwę, ale inne rozszerzenie
.DFM
binarne zbiory z graficznymi obrazami formatek, przy czym jeden projekt może zawierać wiele takich zbiorów
UWAGA: w Delphi 6 i 7 pliki .DFM są plikami tekstowymi lub binarnymi (zależy od opcji), książka odnosi się do Delphi 5
.PAS
zbiory z tekstami źródłowymi modułów, które są związanie z formatkami i zawierają ich kody programowa; a także zbiory z modułami, które nie są powiązane formatkami; (po kompilacji z .PAS wychodzi .DCU)
inne
opcjonalne zbiory dołaczane do programu za pomocą odpowiednyich dyrektyw, na przykład zbiory z zasobami (.RES), .OBJ, .INC

Co to są zdarzenia? W jaki sposób w środowisku Delphi dołącza sie kody obsługi zdarzeń związanych z komponentami?

Zdarzenie
to zjawisko wymagającej reakcji przez program (np. wybór określonego przycisku w okienku lub naciśnięcie klawisza myszki).

Żeby dołączyć w środowisku Delphi kod obsługi zdarzenia komponentu trzeba wybrać komponent, przejść na zakładkę Events w okienku Object Inspector i kliknąć dwukrotnie wybrane zdarzenie. Wtedy w module związanym z daną formatką (na której jest wybrany komponent) dodana zostanie metoda - pojawi się jej szkielet, w której można wpisać kod obsługi zdarzenia. Tą metodę można też wpisać ręcznie (trzeba znać parametry przekazywane do obsługi zdarzenia) i później powiązać ją z wybranym zdarzeniem.

Co to jest własność?

Własność (zwana czasem właściwością, co jest chyba lepszą nazwą)
to cecha obiektu (na przykład napis na przycisku, kolor obiektu, kolor i któr napisu, tytuł i rozmiar okienka).

W jaki sposób można zapisywać i odczytywać wartości własności?

Bezpośrenio lub pośrednio (za pomocą specjalnych metod). Z dostępem bezpośrednim mamy do czynienia gdy specyfikatory read write odnoszą sie bezpośrednio do pól obiektu, a z pośrednia - gdy odwołują się do specjalnych metod.

Z punktu widzenia programisty własności są zwykłymi polami i zmienia się wartości poprzez zwykłe przypisanie.

Ale uwaga: własności nie służą do przechowywania danych, a jedynie określają sposób ich wymiany z obiektem (i ewentualnie przedstawiają czynności wykonywane podczas tej wymiany).

Co to jest formatka?

Formatka
to okienko, na którym podczas projektowania części wizualnych programu okienkowego, czyli łącza graficznego użytkownika, można umieszczać różne komponenty, przy czym jeden program okienkowy może mieć jedną formatkę lub kilka

Co to jest komponent?

Komponent
widoczny (np. przycisk, podokienko wejściowe [a nie jednowierszowe podokienko redakcyjne??], napis) lub niewidoczny (np. powiązanie z bazą danych) element graficznego łącza użytkownika znajdujący się na formatce lub powiązany z formatką (element taki jest reprezentowany przez pewien obiekt programowy).

Podaj ogólną strukturę programu tekstowego i okienkowego w języku Object Pascal

Tekstowy:

program nazwa programu
{$APPTYPE CONSOLE}
deklaracje-modułów
część opisowa
begin
  ciąg instrukcji
end.

Okienkowy:

program nazwa programu
uses
  Forms,
  nazwa-modułu-formatki in 'nazwa-pliku-modułu-formatki'; [może być wiele takich linijek]
{$R *.RES}
begin
  Application.Initialize;
  Application.CreateForm(nazwa-klasy-formatki, nazwa-obiektu-formatki); [może być wiele takich linijek]
  Application.Run
end.

Podaj pełną składnie definicji typu rekordowego

type
  identyfikator-typu = record
    lista-deklaracji-pól
  end

Gdzie każda z deklaracji-pól ma postać:

lista-nazw-pól: opis-typu

a ostatnia deklaracja moze mieć postać (deklaracja wariantowa):

case deklaracja-pola-wyróżnikowego of wykaz-wariantów

lub

case identyfikator-typu-porządkowego of wykaz-wariantów

przy czym deklaracja-pola-wyróznikowego wygląda następująca:

identyfikator-pola-wyróżnikowego: identyfikator-typu-porządkowego

a każdy element wykazu-wariantów ma postać:

lista-etykiet-wybory: (lista-deklaracji-pol)

W programie zdefiniowano typ proceduralny...

type funkcja = function (d: Double): Boolean;

i zadeklarowano zmienne:

var f   : funkcja;
    wsk : Pointer;

Co zostanie przypisane zmiennej f po wykonaniu instrukcj:

f:=funkcja(wsk);

a co spowoduje wykonanie instrukcji

funkcja(wsk):=f;

Przy wykonaniu f:=funkcja(wsk) zostanie przypisana do zmiennej f wartość wskaźnika wsk. Natomiast wykonując funkcja(wsk):=f do zmiennej wskaźnikowej wsk zostanie przypisany adres pamiętany w zmiennej proceduralnej f. W obu przypadkach funckja(wsk) jest zmianą typu zmiennej w odwołaniu, co po prostu umożliwia takie przypisania. Bez tego nie byłyby one możliwe, ponieważ typy wskaźników (bo obie zmienne są w rzeczystości zmiennymi wskaźnikowmyi) nie są ze sobą zgodne.

Do czego służą operatory is i as?

Wyrażenie z operatorem is ma postać:

zmienna-typu-klasowego is odwołanie-do-klasy

Wartością wyrażenia z operatorem is jest wartość logiczna True, gdy obiekt wskazany w wyrażeniu (określony przez zmienną typu klasowego) jest elementem klasy określonej przez odwołanie do klasy lub klasy z niej wyprowadzonej (potomnej). W przeciwnym wypadku, lub gdy zmienna-typu-klasowego ma wartość nil wartością wyrażenie jest False.

Wniosek: opertor is służy do zbadania, czy dany obiekt jest obiektem typu odwołanie-do-klasy.

Wyrażenie z operatorem as ma postać:

zmienna-typu-klasowego as odwołanie-do-klasy

Wyrażenie to pozwala zmienić typ wyspecyfikowanej zmiennej na typ określony drugim operandem, przy czym zmienna ta w dalszym ciągu będzie wskazywała na ten sam obiekt w pamięci. Zmienna określająca obiekt musi mieć albo wartość nil, albo wskazywać na obiekt typu zdefiniowanego przez odwołanie-do-klasy, albo też na obiekt typu potomnego tej klasy. W przeciwnym wypadku wystąpi warunek błędu EInvalidCast.

Wniosek: operator as służy do zamiany (w wyrażeniu tylko) typu zmiennej-typu-klasowego na odwołanie do klasy.

Podaj ogólną postać instrukcji wyboru i opisz jej realizację.

Ogólna postać:

if wyrażenie
  then instrukcja

lub

if wyrażenie
  then instrukcja
  else instrukcja

UWAGA: przed else nie ma średnika, nie może być. Wartością wyrażenie powinna być wartość logiczna True lub False. Instrukcja występująca po słowie kluczowym then lub else może być dowolną instrukcją prostą lub złożoną.

Jeżeli wartością wyspecyfikowanego wyrażenia jest True, to zostanie wykonana instrukcja podana po słowie then. W przeciwnym wypadku wykonana będzie następna instrukcja po instrukcji if (gdy brak jednostki else) lub instrukcja podana po słowie else (po tym else i tak zostnaie wykonana następna instrukcja po instrukcji if - mała nielogiczność w tekście książki :P).

Podaj ogólną postać i opisz realizację instrukcji "dla".

for zmienna:=wyrażenie-1 to wyrażenie-2 do
  instrukcja
for zmienna:=wyrażenie-1 downto wyrażenie-2 do
  instrukcja

Wykonanie:

  1. oblicza się wartość obydwu wyrażeń (w1 i w2)
  2. sprawdza się czy wartość w1 jest: jeżeli jest spełniony ten warunek to wykonywany jest punkt 3, a w przypadku przeciwnym wykonywana jest następna instrukcja po 'dla'
  3. wartość w1 jest podstawiana pod wartośc-sterującą (nazwijmy ją z)
  4. wyknuje się instrukcje po słowie kluczowym do
  5. sprawdza się, czy aktualna wartość z jest różna od w2, gdy jest to prawdziwe to wykonuje się 6, w przeciwnym wypadku wykonywana jest instrukcja następna po 'dla'
  6. zmiennej z przypisuje się:
  7. wykonuje instrukcję po słowie kluczowym do i przechodzi do punktu 5

Przestudiujcie tą definicję porządnie, bo dhAM zawsze przy niej ostro chrząka i będzie wymagał jej dość dokładnie.

Wymień i opisz wszystkie sposoby dostępu do procedur i funkcji bibliotek DLL oraz podaj dla nich odpowiednie przykłady.

Przez nazwę. W tablicy nazw odpowiedniej biblioteki DLL będzie poszkiwany identyfikato danej procedury lub funkcji (po przekształceniu wszystkich małych liter na wielkie). Dyrektywa external ma postać:

external nazwa-biblioteki

procedure proc_a; external 'PROCDLL.DLL';

Poprzez nową nazwę. W takim wypadku należy dodatkowo podać nazwę eksportową procedury lub funkcji w danej bibliotece, która zarazem jest nazwą importową w programie (module, bieżącej bibliotece DLL). Nazwę tę podaje się po słowie (dyrektywie języka) name. Dyrektywa external ma postać:

external nazwa-biblioteki name nazwa-importowa

function podzielnik(a, b: Longint): Longint; external 'NUMLIB.DLL'; name 'NWP';

Poprzez liczbę porządkową. Jest on najbardziej efektywny, gdyż w tym wypadku odpowiedni identyfikator nie jest poszukiwany w tablicy nazw biblioteki. Liczba porządkowa procedury lub funkcji jest w bibliotece DLL ustalona za pomocą klauzuli exports. Dyrektywa external ma postać:

external nazwa-biblioteki index liczba-porządkowa

procedure proc_a; external 'PROCDLL.DLL' index 15;

Podaj sposób deklaracji i opisz zastosowanie parametrów przekazywanych przez stałe i zmienne nieokreślonego typu. Czym te parametry różnią się od parametrów otwartych?

Deklaracje parametrów przekazywanych przez stałe i zmienne nieokreślonego typu mają postać odpowiednio:

var lista-parametrów
const lista-parametrów

Parametry nieokreślonego typu służą głownie do konstrukcji procedur i funkcji przetwarzających podobne struktury danych, ale o różnych rozmiarach.

Dodam, że są to dość dziwne parametry. Nie są zgodne z żadnym typem, ich użycie jest jedynie możliwe przez zmianę typu zmiennej w odwołaniu. Różnią się od parametrych otwartych znacznie. Argumentami, odpowiadającymi parametrom nieokreślonego typu mogą być zmienne (lub wyrażenia) dowolnego typu. Natomiast argumentami odpowiadającymi parametrom otwartym są tablice (ewentualnie pojedyńcza zmienna) dowolnej wielkości, ale określonego typu.

Co ukaże się na ekranie w wyniku wykonania następującego programu?

program Egzamin;
{$APPTYPE CONSOLE}
uses SysUtils;
var w, w1 : Variant;
    i     : Integer;
begin
 w:=VarArrayCreate([1,4],varVariant);
 w[1]:='Object';
 w[2]:=False;
 w[3]:=1.2;
 w[4]:=2;
 for i:=1 to 4 do
    Writeln(w[i]);
 w1:=w;
 w[4]:=VarArrayCreate([1,4],varVariant);
 w[4]:=w1;
 for i:=1 to 3 do
    Writeln(w[i]);
 for i:=1 to 4 do
    Writeln(w[4][i]);
 Readln
end.

Na ekranie zostanie wypisane:

Object
0
1,2
2
Object
0
1,2
Object
0
1,2
2

Warto przy tym zauważyć, że wszystkie elementy tablicy wariantowej (jak i wszystkie zmienne wariantowe) są wypisane w dziwnie ludzkim języku. Zamiast FALSE pojawiło się po prostu 0 oraz zamiast 1.20000000000000E+0000 pokazało się 1,2 (z przecinkiem, a nie kropką - to podobno zależy od ustawień regionalnych).

Wyjaśnij różnicę pomiędzy formatką i komponentem.

Jak na mój gust to różnica jest duża. Formatka (str.23) to okienko, na którym w procesie tworzenia programu można umieszczać różne komponenty i związany jest z nią pewien moduł programowy. Komponent (str.23) to element łącza użytkownika umieszczany na formatce (lub z nią powiązany) i jest reprezentowany przez obiekt programowy.

Wymień i opisz trzy cechy programowania zorientowanego obiektowo.

hermetyczność
struktury danych i kody programowe są połączone w całościową jednostkę programową zwaną obiektem; dzięki temu obiekty mogą zawierać dane wewnętrzne i kody dostępne ogólnie albo ukryte przed innymi obiektami; główną zaletą hermetyczności jest możliwość zabezpieczenia danych przed jednoczesnym dostępem przez różne fragmenty kodu programowego
dziedziczność
umożliwia programiście definiowanie potomków istniejących obiektów, ponieważ potomek dziedziczy własności i metody po swoim przodku określa się w nim jedynie różnice w odniesieniu do przodka, praca programisty koncentruje się zatem na oprogramowaniu własności i metod unikatowych dla przodka; zasada dziedziczności jest też wygodna w przypadku wykonywania zmian obejmujących całą klasę obiektów - wytarczy wówczas je wykonać tylko w obiekcie nadrzędnym, wszystkie obiekty potomne automatycznie będą uwzględniać te zmiany
polimorfizm
umożliwia tworzenie w obiektach potomnych metod o takich samych nazwach jak w obiektach nadrzędnych, ale wykonujących różne czynności, dzięki polimorfizmowi programista może nakazać obiektowi wykonać określoną metodę bez wnikania w wewnętrzne szczegóły dotyczące rodzaju 9typu) danego obiektu

Opisz wszystkie typy łańcuchowe i podaj sposoby ich definiowania.

łańcuchy krótkie
pamięć dla nich jest przydzielana statycznie, ich długość może się zmieniać dynamicznie od 0 do maksymalnej długości podanej w definicji lub przyjmowanej domyślnie (255), pierwszy bajt zmiennej określa zawsze aktualną długość łańcucha
łańcuchy długie
pamięc dla nich jest przydzielana dynamicznie, długość jest ograniczona rozmiarem pamięci przeznaczonej dla programu (do 2GB pamięci), zmienna zawiera wskaźnik do łańcucha, który jest zakończony znakiem #0 (znak ten nie jest częścią łańcucha), jeżeli zmienna nie zawiera łańcucha to jej wartością jest nil
łańcuchy dwubajtowe
reprezentują łańcuchy składające się ze znaków UTF-16 (Unicode), czyli ze znaków dwubajtowych, łańcuchy te są typu WideString, pamięć jest przydzialana dynamicznie, jak dla łańcuchów długich; bezpośrednie wypisywanie takich łańcuchów na ekran nie jest możliwe

Warto zapamiętać, że są łańcuchy krótkie definiowane przez samo słowo kluczowe string przy dyrektywie $H-, albo też przez string[rozmiar] lub identyfikator ShortString, niezależnie od stanu dyrektywy $H. Natomiast łańcuchy długie definiuje się przez słowo kluczowe string przy $H+ lub przez identyfikator AnsiString (niezależnie od $H).

Co to są typy wariantowe? W jaki sposób można sprawdzić aktualny typ zmiennej wariantowej?

Typ Variant to typ, którego elementy mogą zmieniać się podczas wykonywania programu.

Sprawdzenie aktualnego typu można dokonać używając funkcji VarType:

if VarType(wariant) and stała = stała
  then ...

gdzie stała określa kod typu wariantu, który jest złożony ze stałych o przedrostku var

Sprawdzenie czy zmienna nie jest tablicą uzyskuje się poprzez:

if VarType(wariant) or varTypeMask = varTypeMask
  then ...

Zmienną proceduralną xyz zadeklarowano w następujący sposób:

var xyz: function(a: Extended): Extended;

Przypisanie:

xyz:=Sin;

jest błędne (dlaczego?). Co należy zrobić, aby zmienna xyz oznaczała funkcję standardową Sin?

Przypisanie jest niemożliwe ponieważ do zmiennych poceduralnych nie można przypisywać procedur i funkcji z modułu System. Żeby zrealizować cel należy zdefiniować frunkcję:

function Sin(x: Extended): Extended;
begin
  Sin:=System.Sin(x);
end;

Wtedy można przypisać xyz:=Sin;

Co to wyrażenie?

Rozmawiałem z dhAM i najważniejsze dla niego jest to, że każdy element definicji wyrażenia (chodzi mi o składnik, wyr.proste, wyrażenie) jest albo samym elementem stopnia niższego (dla składnika jest to czynnik, ...) albo połączeniem tych elementów odpowiednimi operatorami (dla składnika jest to operator multyplikatywny, ...). Jeśli chodzi o definicję czynnika to wystarczy wymienić najważniejsze (nie wszystko, ale byle bez błędów). Myślę, że najważeniejsze to: odwołanie do zmiennej, wyrażenie w nawiasach, stała bez znaku, czynnik poprzdzony +,-,not, wywołanie funkcji. Oczywiście definicję (lekko okrojoną) należy przedstawić w rekurencyjnej postaci (jak w książce), a przypadkiem nie tak jak przeze mnie (bo to nie jest definicja).

W ogólności wyrażenie jest zbudowane z czycnników, składników i wyrażeń prostych.

Czynnik to:

Składnik to: czynnik lub połaczenie dwu lub większej liczby czynników operatorem multiplikatywnymc (* / div mod and shl shr as).

Wyrażenie proste: jest to składnik lub połączenie dwu lub większej liczby skłądników operaotem addtywnym (+ - or xor).

Wyrażenie to wyrażenie proste lub połączenie dwu wyrażen prostych operatorem relacyjnym (< <= = > >= <> in is).

Co to jest konstruktor? Jak należy go wywołać w celu utorzenia obiektu typu klasowego?

Konstruktory są specjalnym rodzajem metod (definiowanymi przy pomocy constructor), które służą do utworzenia i zainicjowania nowego obiektu. Inicjowanie polega na przekazaniu poprzez parametry różnych wartości do konstruktowa. W przeciwieństwie do zwykłych metod, które wywołuje się przez odwołanie do obiektu, konstruktor może być wywołany zarówno przez odwołanie do obiektu jak i przez odwołanie do klasy.

zmienna-obiektowa:=typ-klasowy.nazwa-konstruktora(lista-parametów)

Lista-parametrów jest opcjonalna - zależy od konstruktora.

Typ x i zmienną y opisano następująco:

type x = array[-2..10,'a'..'z'] of Double;
var z = array[Boolean] of array[-10..-1] of Integer;
  1. Jakie wartości otrzymamy po wywołaniu funkcji: High(x)
  2. Low(x)
  3. High(z)
  4. Low(z)

Po pierwsze nie ma różnicy czy identyfikator jest typem, czy zmienną danego typu - funkcje High i Low zachowują się tak samo. Po drugie High i Low zwracają największą i najmniejszą wartość jakie może przyjąć zmienna podanego typu, a dla tablic zwracają największy i najmniejszy indeks tablicy. Po trzecie te funkcje zwracają wartość typu zgodnego z podanym jako parametr. Jeśli podana zmienna/typ jest tablicą to zwracają wartość zgodną z typem indeksowym tej tablicy. Może być to liczba, znak, True, False lub identyfikator typu wyliczeniowego. Jeśli chodzi o tablicę dwuwymiarową to wygląda to tak: array [-2..10,'a'..'z'] of Double jest tym samym co array [-2..10] of array ['a'..'z'] of Double a to jest array [-2..10] of COŚTAM, - nie interesuje nas czego to jest tablica (nieważne, że jej elementy to też tablice). Podobnie z drugą tablicą: array [Boolean] of array[-10..-1] of Integer to jest array [Boolean] of COŚTAM - analogicznie j.w. Czy już wiecie co będzie wynikiem?

  1. 10
  2. -2
  3. TRUE
  4. FALSE