typ
Transkrypt
typ
Paradygmaty Programowania Wykład 3 Typy dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 1 Typ - definicja Przez typ rozumieć będziemy pewien zbiór wartości, które mogą być przyjmowane przez zmienne. W praktyce z każdym typem związanych jest szereg operacji, które można wykonywać na wartościach z tego typu. Tak postawiona definicja bliska jest klasycznemu pojęciu zbioru (w naszym przypadku działamy na zbiorach skończonych gdyż pamięć jest skończona). Dozwolone operacje na typach, to wszystkie operatory (w szerokim rozumieniu, czyli również podprogramy, podstawienia itp.), których dziedziną jest ów typ lub typ z nim zgodny (tu zgodność można rozumieć jako zawieranie), tj. operacja(typ1) -> typ2. Przykładowo, niemal w każdym języku programowania występuje podstawowy typ związany z liczbami całkowitymi (int, Integer, integer). Jest to skończony podzbiór zbioru liczb całkowitych Z, na ogół odpowiadający zakresowi liczb, jakie można przechowywać w jednym słowie danego komputera. Dozwolone operacje to np. dodawanie, odejmowanie, mnożenie, dzielenie (całkowite i z resztą). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 2 Tworzenie nowych typów Kilka ogólnych kwestii związanych z tworzeniem nowych typów: Typ pierwotny to taki typ, którego w danym języku nie da się zdefiniować za pomocą innych typów, np. typ prosty. Większość języków posiada pewien zestaw typów prostych, np. char, int, float (np. w C lub C++). Z typów pierwotnych możemy tworzyć typy złożone, jak np. rekordy, tablice itp. (np. record i array w Pascal). Można tworzyć typy wyliczeniowe, czyli listy stałych. Ich ważną cechą jest to, że definiując typ jednocześnie określamy liniowy porządek na nim (np. enum w C++ czy Enum Javie). Powyższe mechanizmy to klasyka języków imperatywnych zapoczątkowana przez język programowania Algol. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 3 Typy a Zbiory Oto kilka widocznych analogii pomiędzy typami a zbiorami: Rekord to iloczyn kartezjański typów składowych (przykład Pascal): record = typ1 x typ2 x … x typn Tablica to iloczyn kartezjański odpowiedniej liczby egzemplarzy tego samego typu (przykład C++): tablica = typ x typ x … x typ Typ wyliczeniowy to zbiór zawierający wskazane elementy (choć w istocie definiujemy coś więcej: porządek). A zatem jest to skończony ciąg o ustalonych elementach (przykład Java): enum typ {element1,element2,…,elementn} Podtyp jakiegoś typu to jego podzbiór, dokładniej to typ oparty na typie bazowym i narzucający pewne ograniczenia na ten typ (przykład ADA): subtype nowytyp is typbazowy range zakres Typ pochodny to ”kopia typu pod inną nazwą” (przykład C): typedef nowytyp typbazowy (W niektórych źródłach przez typ pochodny rozumie się typ rozszerzający dany typ, np. klasę. Wtedy zachodzi oczywista relacja nadzbioru). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 4 Po co właściwie typy? Kilka powodów potrzeby istnienia typów: Na poziomie maszynowym wszelkie dane zapisane są jako układy bitów, niezależnie od tego, co reprezentują, a zatem typy to oczywisty sposób nadania znaczenia tym ”anonimowym” układom bitów. Dostajemy możliwość wykrywania wielu powszechnych błędów (przez sprawdzanie zgodności typów). Optymalizacja kodu przez kompilator przez wybór efektywnej optymalizacji (zakresy liczb są znane w czasie kompilacji). Typy abstrakcyjne stanowią sposób na modularyzację programów (typy abstrakcyjne związane są bezpośrednio z pojęciem programowania obiektowego). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 5 Pierwotne typy danych Podstawowe 4 typy proste (pierwotne): Typ całkowity (int, Integer, integer) odpowiada zazwyczaj takiemu zakresowi liczb, jaki mieści się w jednym słowie maszyny (na ogół 4B). Typ ten miewa warianty różniące się rozmiarem (byte, short, long) i dopuszczaniem znaku, tzn. liczb ujemnych (signed/unsigned). Typ zmiennopozycyjny (float, double) to prawie zawsze typ obsługiwany sprzętowo, zgodne ze standardem IEEE 754. Typy znakowy (char, character) przez długi czas wykorzystywał kodowanie ASCII; obecnie coraz częściej używany jest styl kodowania Unicode (2B). Typ logiczny (boolean) jest kodowany za pomocą pojedynczych bitów (co jest oszczędne pamięciowo, ale wolniejsze w dostępie) lub całych bajtów (słów). Występują również pierwotne typy stałopozycyjne, czyli liczby z ustaloną w deklaracji liczbą cyfr i liczbą miejsc po przecinku. Typy takie dobrze nadają się do obliczeń finansowych, pozwalając uniknąć niektórych problemów z zaokrągleniami charakterystycznych dla typów zmiennopozycyjnych. Inne zastosowanie to obliczenia na urządzeniach pozbawionych sprzętowej obsługi liczb zmiennopozycyjnych, np. przenośne urządzenia do gier. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 6 Typ całkowity W jednym z najpopularniejszych języków programowania C, C++ zdefiniowano 8 typów danych opartych na liczbach całkowitych przeznaczonych do reprezentacji liczb całkowitych - są to short int, int, long int, long long int w wersjach ze znakiem (signed) oraz bez znaku (unsigned). Ich rozmiary w bitach zależą od implementacji. Standard C99 określa następujące zależności pomiędzy typami: int ma minimum 16 bitów, long int jest co najmniej takiego rozmiaru, co int, long long int ma minimum 64 bity. W praktyce współczesne kompilatory (takie jak GCC) na maszynach 32bitowych zazwyczaj stosują typy o następujących rozmiarach: short int ma 16 bitów, int jest równy long int i ma 32 bity, long long int ma 64 bity. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 7 Typ zmiennopozycyjny IEEE 754 - standard reprezentacji binarnej i operacji na liczbach zmiennoprzecinkowych (IEEE floating-point standard), implementowany powszechnie w procesorach i programowaniu obliczeniowym. Liczbę pojedynczej precyzji w formacie "IEEE-754" zapisujemy za pomocą trzydziestu dwóch bitów. Pierwszym bitem jest bit znaku S (sign). Jeśli liczba zapisana w kodzie dziesiętnym jest ujemna, oznacza to, iż S przyjmie wartość 1. Jeśli liczba dziesiętna jest dodatnia - to S przyjmuje 0. Dalej następuje 8 bitów kodujących wykładnik potęgi 2 (cecha), oraz 23 bity rozwinięcia binarnego (mantysa), przy czym pomija się wiodący, niezerowy bit. Daje to około 7-8 dziesiętnych miejsc znaczących i zakres od około ±1.18·10-38 do około ±3.4·1038. Zakres taki często jest wystarczający w prostych obliczeniach. Oprócz tego zdefiniowano szczególne przypadki: +0 - wszystkie bity są zerami, -0 - bit znaku jest ustawiony, reszta jest zerami, liczby małe (tj. bliskie zera) - wykładnik równy zero, mantysa różna od 0, nie zakłada się wiodącego niezerowego bitu; są to liczby zbyt małe aby mogły być reprezentowane z taką samą precyzją jak "zwykłe" liczby, ±∞ - ustawione wszystkie bity wykładnika, mantysa równa 0, może się pojawić np. jako wynik dzielenia przez 0, NaN (z ang. Not a Number) ustawione wszystkie bity wykładnika, mantysa różna od 0, może się pojawić np. jako wynik pierwiastkowania liczby ujemnej. Zobacz: http://eff10.internetdsl.tpnet.pl/programowanie/kurs_sc/liczby/pages/14.htm dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 8 Typ znakowy W językach C i C++ typ ten jest po prostu jednobajtową liczbą całkowitą (może być ona ze znakiem, w przypadku signed char lub bez znaku, dla unsigned char w przypadku nie podania kwalifikatora signed/unsigned rodzaj liczby zależy od implementacji). W Pascalu natomiast typ znakowy nie jest typem liczbowym (do konwersji używa się funkcji Chr). Wewnętrznie jednak znak jest zawsze reprezentowany przez jego kod, w zależności od implementacji będzie to ASCII, czy Unicode itp. Zapis konkretnej wartości typu znakowego w kodzie źródłowym dokonuje się za pomocą literału znakowego. Typy znakowe w językach programowania: C/C++ - char, signed char, unsigned char, Pascal - Char, Fortran - character, Java - char, Character, Ada - Character, itp. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 9 Typ logiczny Typ logiczny to typ obejmujący dwie wartości: false i true. Każda zmienna logiczna zajmuje w pamięci komputera 1 bajt. Wartość zmiennej jest przechowywana w najmłodszym bicie. Pozostałe 7 bitów jest niewykorzystywane. C/C++ - bool (lub typ całkowity), Pascal - Boolean, Java - boolean, Ada - Boolean jako Enumeration typ, itp. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 10 Złożone typy danych Omówimy teraz główne złożone typy danych (typy budowane w oparciu o typy pierwotne), tj: napisy (teksty, łańcuchy), tablice, tablice asocjacyjne (mapy, słowniki), rekordy (struktury), wskaźniki (referencje), typy abstrakcyjne (klasy). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 11 Napisy (teksty, łańcuchy) Typ napisowy, to typ powstały w oparciu o typ znakowy (char). W niektórych językach programowania typ napisowy może być typem pierwotnym (np. w Javie klasa String). W wielu językach, np. w C napisy to szczególne rodzaje tablic, a zatem nie są typem pierwotnym: char[] napis = ”ala”; napis[0]=’a’, napis[1]=’l’, napis[2]=’a’, napis[3]=’\0’ Cechy napisów o zmiennej długości: Napisy statyczne, tj. po zadeklarowaniu nie można zmienić ich długości oraz zawartości (np. w Javie obiekty klasy String). Napisy dynamiczne o długości ograniczonej statycznie, tj. deklarujemy napis o ustalonej długości (np. tablica znakowa w C czy C++). Napisy w pełni dynamiczne, czyli długość może się zmieniać bez ograniczeń (np. napisy w Perlu). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 12 Tablice - implementacja Tablica to zestaw elementów takiego samego typu, gdzie dostęp do poszczególnych elementów jest poprzez indeksowanie. Wymaga to dynamicznego wyliczania adresu elementu (chyba że indeks jest stałą znaną w czasie kompilacji). Problemy napotykane podczas implementacji, to: Kiedy i skąd alokowana jest pamięć dla tablicy? Generalnie tak jak dla zwykłych zmiennych, choć języki obiektowe skłaniają się do alokowania tablic dynamicznie, ze sterty (np. Java). Jakiego typu mogą być indeksy? Generalnie są one podtypu typu całkowitego ale mogą też być typu dającego się odwzorować na zakres liczb całkowitych, np. typu znakowego czy typu wyliczeniowego. Czy dopuszczamy tablice wielowymiarowe? Czy dopuszczamy operacje indukowane z operacji na elementach? W pewnych sytuacjach takie operacje byłyby naturalne, np. dodawanie dwóch macierzy za pomocą naturalnego operatora ”+”. Tego typu operacje oferuje Fortran. W C, czy C++ można odpowiednio przeciążać operatory ”+”, ”-” itp. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 13 Tablice - implementacja Tłumacząc instrukcje z odwołaniami do elementów tablicy kompilator generuje kod wyliczający adres elementów. Odbywa się to następująco: Dla tablic jednowymiarowych tab (n elementów) adres elementu tab[i] otrzymujemy następująco: (adres elementu tab[0]) + i * (rozmiar elementu) przy założeniu indeksowania od 0. Dla tablic wielowymiarowych tab (m x n elementów) (rozumianych jako tablice tablic jednowymiarowych, w dwóch możliwych wariantach, tj. ułożonych wierszami lub kolumnami) adres elementu tab[i][j] (przy założeniu, że ułożenie występuje wierszami) otrzymujemy następująco: (adres elementu tab[0][0]) + (i*n+j) * (rozmiar elementu) przy założeniu indeksowania od 0 obu wymiarów. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 14 Tablice asocjacyjne (mapy, słowniki) Tablica asocjacyjna to nieuporządkowany zestaw elementów (wartości) identyfikowanych za pomocą kluczy. Istotą rzeczy jest to, że klucze mogą pochodzić z obszernego zbioru możliwych wartości (zwykle są to dowolne napisy). Nie ma zatem prostego odwzorowania kluczy na adresy elementów tablicy. Tablice asocjacyjne są użyteczne tam, gdzie potrzebny jest swobodny (nieuporządkowany) dostęp do elementów. Tam, gdzie potrzebujemy pętli przetwarzającej wszystkie elementy po kolei, bardziej efektywna jest zwykła tablica. Typowy przykład tablic asocjacyjnych pojawia się w Perlu, Python i PHP. Również w Javie takim przykładem mogą być mapy, tj. HashMap lub TreeMap. Implementacja tablicy asocjacyjnej musi przechowywać nie tylko wartości elementów, ale również ich klucze. Typowy sposób implementacji to użycie funkcji haszującej do odwzorowania kluczy na adresy. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 15 Rekordy (struktury) Rekord to zestaw elementów dowolnych typów. Elementy rekordu zwane są polami. Większość języków stosuje zapis „z kropką” na oznaczenie dostępu do pól rekordu. Rekordy przechowywane są w pamięci w kolejnych komórkach, choć architektura sprzętu może narzucać wymóg umieszczania niektórych pól pod adresami będącymi wielokrotnością np. czterech. To może powodować luki pomiędzy polami. Typowe kwestie i trudności związane z implementacją rekordu, to: Czy potrzeba jest pamiętać o położeniu pól rekordów? Położenie pól w obrębie rekordu jest znane w czasie kompilacji, zatem nie jest potrzebne przechowywanie informacji o położeniu pól w trakcie wykonania. Jak zinterpretować znak ”=” dla dwóch rekordów? Naturalna wydaje się implementacja operatora równości rekordów rozumianej jako koniunkcja równości poszczególnych pól. Zauważmy, że z uwagi na luki między polami nie można tego zrealizować jako porównanie bloków pamięci. Co zrobić z napisami o zmiennej długości; trzeba porównywać jedynie faktycznie wykorzystywaną część napisu. Jak traktować pola będące wskaźnikami; czy aby nie chcielibyśmy, by porównania dotyczyły wskazywanych obiektów, a nie samych wskaźników (czy też referencji)? Czy rekordy to aby nie klasy? Struktury w C są zwykłymi rekordami, natomiast w C++ są w istocie klasami (jest mała różnica w widoczności pól). Istotna staje się tutaj bliskość tego typu do typu klasowego. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 16 Wskaźniki (referencje) Typ wskaźnikowy obejmuje wartości, które mogą wskazywać inne wartości w pamięci, oraz dodatkową wartość ”pustą”, która jest inna niż jakikolwiek ”prawdziwy” wskaźnik. Wartość ”pusta” bywa często oznaczana jako null lub nil. Z technicznego punktu widzenia wskaźniki są to adresy komórek pamięci. Różnica między wskaźnikiem a adresem to dodatkowa informacja o typie wskazywanych obiektów, którą posiada kompilator (i wykorzystuje do sprawdzania zgodności typu). Motywacją do używania wskaźników jest możliwość dynamicznego zarządzania pamięcią oraz elastyczność, jaką daje adresowanie pośrednie. W szczególności, wskaźniki są jedynym sposobem korzystania ze zmiennych alokowanych dynamicznie na stercie. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 17 Operacje na wskaźnikach Pewne aspekty operacji wykonywanych na wskaźnikach: Potrzebne jest przypisanie i dereferencja wskaźnika, czyli dostęp do elementu wskazywanego przez wskaźnik (na ogół *). Do zarządzania pamięcią potrzebny jest mechanizm alokacji, np. new, malloc. Adresowanie pośrednie wymaga posiadania operatora pobrania adresu (wskazania) zmiennej (operator & w języku C). Arytmetyka na wskaźnikach, czyli możliwość swobodnego przesuwania wskaźnika, daje programiście duże możliwości, ale jest niebezpieczna: łatwo sięgnąć do ”nie swojej” pamięci (np. w C i C++). Bez możliwości przesuwania wskaźnika przez co mechanizm staje się bezpieczniejszy (np. Java). Oczywiste problemy pojawiąjące się podczas takich operacji, to: Wiszący wskaźnik, tj. wskaźnik odnoszący się do zmiennej, która została już zdealokowana. Ten problem nie występuje w Javie, gdzie nie ma jawnej dealokacji. Zgubiona zmienna, tj. zmienna na stercie do której nie mamy żadnego wskaźnika. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 18 Wskaźniki a referencje W wielu językach nie ma wskaźników ale są referencje, które częściowo pełnią podobną rolę do wskaźników. Generalnie przez typy referencyjne rozumiemy typy wskaźnikowe o ograniczonych możliwościach. Na większości maszyn wskaźniki mają rozmiar jednego słowa. Oczywiście referencje to to samo co wskaźniki w sensie odwołania do komórek pamięci. W wielu językach w których są referencje a nie ma wskaźników. dealokacja odbywa się na ogół za pomocą mechanizmu zbierania śmieci (garbage collector) . Polega to na tym, że przeglądamy stertę w poszukiwania obiektów do których nie ma referencji i zwalniania bloków do których nie ma żadnych odwołań. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 19 Abstrakcyjny typ danych (klasa) Abstrakcja to reprezentacja pewnego bytu, w której pominięto nieistotne w danym kontekście szczegóły. Pozwala nam to grupować byty według ich wspólnych cech i w zależności od potrzeby (kontekstu) i pozwala nam to albo zajmować się całymi grupami (czyli owymi wspólnymi cechami), albo bytami wewnątrz grupy (czyli szczegółami różniącymi byty w grupie). Chodzi oczywiście o to, by poradzić sobie ze złożonością problemów. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 20 Od czego właściwie abstrahujemy? Abstrakcja procesu - abstrakcjami procesów są podprogramy. Pozwalają wskazać (przez ich wywołanie), że pewna czynność ma być wykonana, bez wskazywania jak ma być wykonana. Szczegóły znajdują się w treści podprogramu, której wywołujący nie musi znać. Abstrakcja danych - czyli zamknięta całość obejmująca reprezentację pewnego typu danych wraz z podprogramami, umożliwiającymi działanie na tych danych. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 21 Czym jest abstrakcyjny typ danych? Abstrakcyjny typ danych, to konstrukcja języka programowania, w której definiujemy typ (w dotychczasowym rozumieniu) oraz operacje na nim w taki sposób, że inne byty w programie nie mogą manipulować danymi inaczej niż za pomocą zdefiniowanych przez nas operacji. Istotą rzeczy jest tu oddzielenie części ”prywatnej” typu (czyli szczegółów reprezentacji danych i implementacji poszczególnych operacji) od części ”publicznej” (tego, co można wykorzystywać w innych miejscach programu). Rozdzielenie składników abstrakcyjnego typu danych na część prywatną i publiczną jest możliwe za pomocą zawartych w języku programowania mechanizmów sterowania dostępem. Dane w typie abstrakcyjnym zwane są właściwościami lub danymi składowymi; używa się też określeń pole lub po prostu zmienna. Operacje zwane są metodami lub funkcjami składowymi. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 22 Abstrakcyjny typ danych - przykłady Wbudowane w języki programowania typy proste można uznać za abstrakcyjne: nie mamy dostępu do reprezentacji wewnętrznej, więc możemy posługiwać się jedynie tym, co dostarcza język. Zdefiniowany w Pascalu typ rekordowy i kilka procedur na nim działających nie stanowi abstrakcyjnego typu danych. Ktokolwiek zadeklaruje rekord tego typu, będzie mógł działać bezpośrednio na tym rekordzie z pominięciem ”oficjalnych” procedur. Sztandarowy przykład to klasa np. z Javy lub C++. Dane schowane w części prywatnej klasy nie są dostępne na zewnątrz, stąd nie da się wykonywać żadnych operacji bezpośrednio. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 23 Czym jest obiekt? Obiekt, to instancja abstrakcyjnego typu danych, czyli pojedynczy ”egzemplarz” tego typu zaalokowany na stercie (najczęściej), na stosie lub statycznie (najrzadziej). Sam abstrakcyjny typ danych w większości języków zwany jest klasą. Każdy obiekt ma trzy charakterystyczne cechy: tożsamość, czyli cechę umożliwiającą jego identyfikację i odróżnienie od innych obiektów, stan, czyli aktualny stan danych składowych, zachowanie, czyli zestaw metod wykonujących operacje na tych danych. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 24 Jaki powinien być abstrakcyjny typ danych? Deklaracja części publicznej (interfejsu), czyli danych i podprogramów przeznaczonych do wykorzystania przez inne byty w programie, powinna mieścić się w jednej jednostce syntaktycznej. Pozostała część, czyli ciała podprogramów publicznych (implementacja) oraz dane i podprogramy przeznaczone do wewnętrznego użytku, może mieścić się w tej samej lub innej jednostce syntaktycznej. Inne byty w programie mogą tworzyć zmienne definiowanego typu abstrakcyjnego. Jedynymi bezpośrednimi operacjami (także w sensie dostępu do zmiennych) na obiektach tego typu są te zawarte w części publicznej. dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 25 Mechanizmy danego języka Mechanizmy języka do tworzenia abstrakcyjnych typów: Jednostka syntaktyczna mieszcząca definicję typu abstrakcyjnego. Sposób wyszczególnienia danych i podprogramów publicznych. Kilka podstawowych, wbudowanych operacji na obiektach z typu, np. podstawienie, sprawdzenie równości, wypisywanie reprezentacji obiektu. Pewne operacje są potrzebne niemal w każdym typie, są jednak zależne od szczegółów tego typu. Muszą zatem być implementowane przez programistę. Są to np. konstruktory, destruktory, iteratory. Język może oferować abstrakcyjne typy danych wprost (C++, C#, Java) lub może mieć bardziej ogólne konstrukcje (np. Ada). dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 26 Ada - przykład implementacji package Stos is type typ_stosowy is limited private; maks_rozmiar: constant := 1000; function pusty(st: in typ_stosowy) return Boolean; procedure poloz(st: in out typ_stosowy; elem: in Integer); procedure zdejmij(st: in out typ_stosowy); function wierzcholek(st: in typ_stosowy) return Integer; private type typ_listowy is array (1..maks_rozmiar) of Integer; type typ_stosowy is record list: typ_listowy; wsk_stosu: Integer range 0..maks_rozmiar := 0; end record; end Stos; dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 27 C++ - przykład implementacji class stos { private: int *baza; int maks_rozmiar, wsk_stosu; public: stos() { baza = new int [1000]; maks_rozmiar = 999; wsk_stosu = -1; } ~stos() {delete [] baza;} void poloz(int elem) {...} void zdejmij() {...} int wierzcholek() {...} bool czypusty() {...} }; dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 28 Java - przykład implementacji class Stos { private int baza[]; private int maks_rozmiar, wsk_stosu; public Stos() { baza = new int[1000]; maks_rozmiar = 999; wsk_stosu = -1; } public void poloz(int elem) {...} public void zdejmij() {...} public int wierzcholek() {...} public boolean czyPusty() {...} } dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 29 C# - przykład implementacji class Stos { private int[] baza; private int maks_rozmiar, wsk_stosu; public Stos() { baza = new int[1000]; maks_rozmiar = 999; wsk_stosu = -1; } public void poloz(int elem) {...} public void zdejmij() {...} public int wierzcholek() {...} public bool czyPusty() {...} } dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 30 Dziękuję za Uwagę!!! dr Robert Kowalczyk, Katedra Analizy Nieliniowej, WMiI UŁ 31