background image

 

 

Typy pochodne 1

Wstęp

Często dogodnie jest wprowadzić nowy typ, który 

jest podobny do istniejącego, niemniej jednak 

różny

.

Niech T będzie pewnym typem. Możemy napisać:

type S is new T;

W takim przypadku mówimy, że S jest 

typem 

pochodnym

 (

derived type

) typu T, który 

nazywamy 

typem macierzystym

 (

parent type

).

Mówimy czasami, że S należy do tej samej 

klasy

 

co typ T.

background image

 

 

Typy pochodne 2

Jeżeli T jest typem rekordowym, to typ S jest też 

typem rekordowym, a jego składowe mają te 
same identyfikatory.

Zbiór wartości typu pochodnego 

jest kopią

 zbioru 

wartości typu macierzystego. Oznacza to, że 

są 

to różne typy

 i 

nie można

 wartości jednego 

typu podstawiać do obiektów drugiego typu. 
Konwersja jest jednak możliwa.

Zapis literałów i agregatów typu pochodnego jest 

taki taki sam, oraz domyślne wyrażenia 
początkowe typu, albo jego składowych są takie 
same jak w przypadku typu macierzystego.

background image

 

 

Typy pochodne 3

Operacje podstawowe

Definicja. Do 

operacji podstawowych

 typu 

zaliczamy:

1. Zdefiniowane wstępnie operacje podstawienia, 

zdefiniowana wstępnie relacja równości, 

odpowiednie atrybuty.

2. W przypadku typu pochodnego, operacjami 

podstawowymi są operacje podstawowe 

odziedziczone po typie macierzystym, które 

mogą być ponownie zdefiniowane.

3. W przypadku typu zadeklarowanego w pakiecie 

definicyjnym, podprogramy zadeklarowane w 

tym pakiecie, posiadające parametry formalne, 

lub wynik tego typu.

background image

 

 

Typy pochodne 4

W

artości typu wyliczeniowego są także operacjami 

podstawowymi, ponieważ są traktowane jak 
identyfikatory funkcji bezparametrowych o 
wartościach typu wyliczeniowego.

Przykład.

 W przypadku typu Boolean, literały False 

i True są operacjami podstawowymi, ponieważ są 
traktowane jakby były funkcjami, takimi jak:
function True return Boolean is
begin
  return Boolean’Val(1);
end;

background image

 

 

Typy pochodne 5

Ogólna idea jest taka, że typ pochodny posiada pewne 

operacje podstawowe, dziedziczone po typie 

macierzystym i można do zbioru tych operacji dodać 

nowe operacje podstawowe.

Przykład. 

Pakiet definicyjny Wektory_Na_Plaszczyznie.
Program Test_Wektory_Na_Plaszczyznie.

Operacje dziedziczone 

mogą być zastąpione

 przez nowe 

operacje.

Rozumiemy przez to zastąpienie operacji dziedziczonych 

przez 

jawnie

 zadeklarowane podprogramy o tych 

samych identyfikatorach jak podprogramy należące do 

zbioru operacji podstawowych typu macierzystego, 

przy czym podprogramy zastępujące 

muszą być

 

zadeklarowane w tym samym obszarze deklaracji, w 

którym definiowany jest typ pochodny. 

background image

 

 

Typy pochodne 6

Deklarując typ pochodny 

należy

 przestrzegać dwóch 

zasad:

1.

Nie można tworzyć typu pochodnego z typu 

prywatnego przed podaniem pełnej deklaracji tego 

typu.

2.

Jeżeli tworzymy typ pochodny w tym samym pakiecie 

definicyjnym, w którym deklarujemy typ macierzysty, 

to typ pochodny dziedziczy wszystkie operacje po typie 

macierzystym i nie można dodać nowych operacji do 

typu macierzystego po deklaracji typu pochodnego.

Mimo że, każdy typ pochodny jest różny, to dzięki 

pokrewieństwu typów pochodnych, wyprowadzonych 

od wspólnego przodka, wartość jednego typu 

pochodnego można łatwo zamienić na wartość innego 

typu powstałej klasy. 

background image

 

 

Typy pochodne 7

Przykład.

 Niech będą dane deklaracje:

type Light is new Colour;
type Signal is new Colour;
type Flare is new Signal;

Typy te tworzą hierarchię typów, która zaczyna się 

od typu Colour. Możemy swobodnie dokonywać 
konwersji wartości tych typów. Na przykład 
możemy pisać:
L : Light;
F : Flare;
  ...
F := Flare(L);

zamiast

 F := Flare(Signal(Colour(L)));

background image

 

 

Typy pochodne 8

Podstawową zaletą

 wprowadzania typów 

pochodnych jest unikanie mieszania obiektów, 

koncepcyjnie należących do różnych typów.

Przykład. 

Załóżmy,  że  chcemy  liczyć  jabłka  i  pomarańcze. 

W tym celu możemy napisać
type Apples is new Integer;
type Oranges is new Integer;
...
No_Of_Apples : Apples;
No_Of_Oranges : Oranges;

Obydwa typy pochodzą od typu Integer, dzięki czemu 

obydwa dziedziczą operację dodawania, co pozwala pisać
No_Of_Apples := No_Of_Apples + 1;
No_Of_Oranges := No_Of_Oranges + 1;

background image

 

 

Typy pochodne 9

Nie wolno oczywiście pisać:

No_Of_Apples := No_Of_Oranges;

ale zamiast tego trzeba użyć  konwersji

 No_Of_Apples := Apples'(No_Of_Oranges);

Przypuśćmy,  że  dwie  procedury  obsługują  sprzedaż  obydwu 

rodzajów owoców

procedure Sell (N : Apples);

procedure Sell (N : Oranges);

Możemy wywołać jedną z nich

Sell (N : No_Of_Oranges);

natomiast wywołanie

Sell (6);

jest  niejednoznaczne  i  w  celu  usunięcia  tej  niejednoznaczności 

trzeba pisać
Sell (Oranges'(6));

background image

 

 

Typy pochodne 10

Jeżeli podprogram jest dziedziczony, to w 

rzeczywistości 

nie jest tworzony nowy 

podprogram

Wywołanie podprogramu dziedziczonego jest 

wywołaniem podprogramu macierzystego, przy 
czym parametry rodzajów in i in out są niejawnie 
konwertowane na typ macierzysty tuż 

przed

 

wywołaniem, a parametry rodzajów in out i out są 
konwertowane niejawnie zaraz 

po

 wywołaniu 

podprogramu.

Pisząc

My_Apples + Your_Apples

mamy

Apples(Integer(My_Apples) + Integer(Your_Apples))

background image

 

 

Typy pochodne 11

Zajmijmy się teraz ograniczeniami. Możemy tworzyć typy 

pochodne ograniczone.

Przykład.

type Probability is new Float range 0.0..1.0;

Jest to równoważne dwóm deklaracjom:

type Anonim is new Float;
subtype Probability is Anonim range 0.0..1.0;

Oznacza to, że podtyp Probability jest podtypem 

ograniczonym anonimowego typu pochodnego. Zauważmy, 
że zbiór wartości typu pochodnego jest kopią zbioru 
wartości typu macierzystego Float. Operacje "+" , ">" i 
inne, działają w całym nieograniczonym zbiorze wartości.

background image

 

 

Typy pochodne 12

Przykład. 

Niech

P : Probability;

Można napisać

 P > 2.0

Mimo, że 

nie można

 podstawić wartości 2.0 do zmiennej P. 

Podane wyrażenie jest zawsze nieprawdziwe, chyba że 

zmienna P nie jest zainicjowana odpowiednio i przez 

przypadek ma złą wartość.

Rozpatrzmy teraz ograniczenia występujące w przypadku 

dziedziczonych podprogramów.

Podprogram odziedziczony

 jest podprogramem macierzystym 

w którym wszystkie egzemplarze (

instances

) typu 

macierzystego 

są wymienione

 na typ pochodny.

Podtypy są wymienione na równoważne podtypy z odpowiednimi 

ograniczeniami, a domyślne wyrażenia inicjujące są 

konwertowane przez dodanie konwersji typów.

Dowolny parametr, albo wynik innego typu pozostaje 

niezmieniony.

background image

 

 

Typy pochodne 13

Przykład.

 Niech

type T is ...;
subtype S is T range L..R;
function F (X : T; Y : T := E; Z : Q) return S;

Przy czym E jest wyrażeniem inicjującym typu T, natomiast typ 

Q nie należy do klasy T, a więc jest typem całkowicie 

niezwiązanym.

Jeżeli napiszemy

type TT is new T;

to z tego wynika, że napisaliśmy też

 subtype SS is TT range TT(L)..TT(P);

a nagłówek dziedziczonej funkcji F ma postać

 function F (X : TT; Y : TT := TT(E); Z : Q)
return SS;

background image

 

 

Typy pochodne 14

W nagłówku typ T został zastąpiony 

przez

 TT, podtyp S 

przez

 SS, 

dokonana została 

konwersja wyrażenia

 E na wartość typu TT, 

natomiast typ Q 

został niezmieniony

. Warto zauważyć, że 

identyfikatory parametrów formalnych 

zostały takie same

.

Typy pochodne są pewną alternatywą do typów prywatnych. Typy 

pochodne 

mają zaletę

 dziedziczenia literałów, ale często 

mają wadę

 dziedziczenia zbyt wielu rzeczy po typie 

macierzystym.

Przykład.

 Weźmy pod uwagę deklaracje

type Length is new Float;
type Area is new Float;

Wprowadzenie tych typów zabezpiecza przed mieszaniem 

długości i powierzchni, ale dziedziczona jest możliwość 

mnożenia dwóch egzemplarzy typu Length, w wyniku czego 

dostaniemy wartość typu Length. Poza tym, dziedziczymy 

wiele nieistotnych operacji jak np. potęgowanie.

background image

 

 

Typy pochodne 15

Takie niepożądane operacje można, jeżeli trzeba, zastąpić 

podprogramami abstrakcyjnymi

, takimi jak:

function "*" (Left, Right : Length) return 

Length 

is abstract;

Podprogram abstrakcyjny 

nie ma treści 

nie można go 

wywołać

. Każda próba wywołania wykrywana jest jako błąd 

podczas kompilacji.

Zalecenie

. Jeżeli jest wiele niepożądanych operacji 

dziedziczonych po typie macierzystym, często lepiej użyć 
typu prywatnego i zdefiniować te operacje których 
potrzebujemy.

Zadanie

. Zadeklarować pakiet Metrics zawierający deklaracje 

typów pochodnych Length i Area wraz z odpowiednimi 
nagłówkami różnych operacji "*", "/", "**".


Document Outline