Rok akademicki 2016/2017, Wykład nr 5 2/40 Plan wykładu nr 5 Informatyka 2 Politechnika Białostocka - Wydział Elektryczny Elektrotechnika, semestr III, studia stacjonarne I stopnia Rok akademicki 2016/2017 Programowanie obiektowe w języku C++ przeładowanie (przeciążenie) operatorów dziedziczenie funkcje wirtualne (polimorfizm) Definicje systemu operacyjnego Zarządzanie procesami definicja procesu, blok kontrolny procesu dwu- i pięciostanowy model procesu Wykład nr 5 (23.11.2016) Rok akademicki 2016/2017, Wykład nr 5 3/40 Rok akademicki 2016/2017, Wykład nr 5 4/40 Przeładowanie (przeciążanie) operatorów Przeładowanie operatorów w klasie przeładowanie operatora polega na nadaniu mu specjalnego znaczenia w momencie, gdy stoi on obok obiektu jakiejś klasy przeładowanie operatora dokonuje się definiując funkcję o postaci: obiekta operator obiektb klasa obiekta, obiektb, obiektc; obiektc = obiekta + obiektb; operator (wymaga napisania własnej funkcji) operator (może wymagać napisania własnej funkcji) obiektc = obiekta + 5; obiektc = 5 + obiektb; operator (wymaga napisania własnych funkcji)
Rok akademicki 2016/2017, Wykład nr 5 5/40 Rok akademicki 2016/2017, Wykład nr 5 6/40 Przeładowanie operatorów w klasie Funkcja przeładowująca operator można przeładować praktycznie wszystkie operatory nie można wymyślać swoich operatorów nie można zmieniać priorytetu operatorów automatycznie tworzone są operatory: przypisania (=) pobrania adresu (&) new, new [], delete i delete [] (tworzenie i usuwanie obiektów) ten sam operator można przeładować wielokrotnie, ale za każdym razem funkcja operatorowa musi mieć inny typ lub kolejność argumentów definiowana jako funkcja składowa klasy obiekta @ obiektb klasa klasa::operator @ (klasa obiektb)... do funkcji przekazywany jest tylko jeden argument (obiektb), argument obiekta przekazywany jest domyślnie przez wskaźnik this funkcja operatorowa, która jest składową klasy wymaga, aby obiekt stojący po lewej stronie operatora był obiektem tej klasy, np. obiekta + obiektb - można przeładować obiekta + 5 - można przeładować 5 + obiektb - nie można przeładować!!! Rok akademicki 2016/2017, Wykład nr 5 7/40 Rok akademicki 2016/2017, Wykład nr 5 8/40 Funkcja przeładowująca operator Przykład nr 1 - klasa kwadrat definiowana jako funkcja globalna (zaprzyjaźniona z klasą) obiekta @ obiektb klasa operator @ (klasa obiekta, klasa obiektb)... aby funkcja globalna mogła korzystać z pól prywatnych klasy musi być funkcją zaprzyjaźnioną z klasą: friend klasa operator @ (klasa obiekta, klasa obiektb); umieszczone w definicji klasy operatory >> i << można przeładowywać tylko jako funkcje globalne Dane składowe klasy: długość boku (a) Funkcje składowe klasy / globalne (zaprzyjaźnione z klasą - friend): konstruktor przeładowanie operatora + przeładowanie operatora (friend) przeładowanie operatora << (friend) przeładowanie operatora >> (friend) przeładowanie operatora ++ (pre- i postinkrementacji) Funkcje składowe klasy (nie ma konieczności ich definiowania): konstruktor kopiujący destruktor przeładowanie operatora =
Rok akademicki 2016/2017, Wykład nr 5 9/40 Rok akademicki 2016/2017, Wykład nr 5 10/40 Przykład nr 1 - klasa kwadrat (1/6) Przykład nr 1 - klasa kwadrat (2/6) #include <iostream> using namespace std; #include <cmath> class kwadrat float a; kwadrat(float bok); kwadrat operator + (kwadrat kw2); friend kwadrat operator - (kwadrat kw1, kwadrat kw2); friend ostream & operator << (ostream & ekran, kwadrat kw); friend istream & operator >> (istream & klawiatura, kwadrat & kw); kwadrat operator ++ (); // preinkrementacja kwadrat operator ++ (int); // postinkrementacja kwadrat(const kwadrat & kw); ~kwadrat(); kwadrat & operator = (const kwadrat & kw); ; kwadrat::kwadrat(float bok=0) a = bok; kwadrat kwadrat::operator + (kwadrat kw2) float bok = a + kw2.a; kwadrat kw(bok); return kw; kwadrat operator - (kwadrat kw1, kwadrat kw2) float bok = kw1.a - kw2.a; if (bok < 0) bok = 0; kwadrat kw(bok); return kw; konstruktor przeładowanie operatora + (funkcja składowa klasy) przeładowanie operatora - (funkcja globalna - friend) Rok akademicki 2016/2017, Wykład nr 5 11/40 Rok akademicki 2016/2017, Wykład nr 5 12/40 Przykład nr 1 - klasa kwadrat (3/6) Przykład nr 1 - klasa kwadrat (4/6) ostream & operator << (ostream & ekran, kwadrat kw) ekran << "[a = " << kw.a << "]"; return ekran; przeładowanie operatora << (funkcja globalna) kwadrat kwadrat::operator ++ () a = a + 1; return *this; przeładowanie operatora ++ (preinkrementacji) istream & operator >> (istream & klawiatura, kwadrat & kw) cout << "a: "; klawiatura >> kw.a; return klawiatura; przeładowanie operatora >> (funkcja globalna) kwadrat kwadrat::operator ++ (int) a = a + 1; return *this; przeładowanie operatora ++ (postinkrementacji)
Rok akademicki 2016/2017, Wykład nr 5 13/40 Rok akademicki 2016/2017, Wykład nr 5 14/40 Przykład nr 1 - klasa kwadrat (5/6) Przykład nr 1 - klasa kwadrat (6/6) kwadrat::kwadrat(const kwadrat & kw) a = kw.a; kwadrat::~kwadrat() kwadrat & kwadrat::operator = (const kwadrat & kw) if (&kw == this) return *this; a = kw.a; return *this; konstruktor kopiujący destruktor przeładowanie operatora = (funkcja składowa klasy) int main() kwadrat K1(8), K2(6), K3; cout << "K1: " << K1 << endl; cout << "K2: " << K2 << endl; cout << "K3: " << K3 << endl; K3 = K1 + K2; cout << "K3 = K1 + K2: " << K3 << endl; K3 = K1 - K2; cout << "K3 = K1 - K2: " << K3 << endl; K3 = K2 - K1; cout << "K3 = K2 - K1: " << K3 << endl; ++K1; cout << "++K1: " << K1 << endl; K1++; cout << "K1++: " << K1 << endl; cin >> K3; cout << "K3: " << K3 << endl; K1: [a = 8] K2: [a = 6] K3: [a = 0] K3 = K1 + K2: [a = 14] K3 = K1 - K2: [a = 2] K3 = K2 - K1: [a = 0] ++K1: [a = 9] K1++: [a = 10] a: 12 K3: [a = 12] Rok akademicki 2016/2017, Wykład nr 5 15/40 Rok akademicki 2016/2017, Wykład nr 5 16/40 Dziedziczenie Dziedziczenie - przykłady dziedziczenie jest to technika pozwalającą na definiowanie nowej klasy przy wykorzystaniu klasy już istniejącej polega na przejmowaniu jednej klasy (bazowej, podstawowej) przez inną klasę (pochodną) przy dziedziczeniu, w skład obiektów klasy pochodnej automatycznie wchodzą pola klasy bazowej osoba imie nazwisko wiek do obiektów klasy pochodnej możemy stosować operacje zdefiniowane przez funkcje składowe klasy bazowej semestr wydzial student wykladowca katedra pensum doktorant opiekun_naukowy dziedziczenie wielobazowe
Rok akademicki 2016/2017, Wykład nr 5 17/40 Rok akademicki 2016/2017, Wykład nr 5 18/40 Dziedziczenie - przykłady Dziedziczenie przykładowa klasa podstawowa i klasa pochodna samochod... osobowy ciezarowy...... /* klasa podstawowa */ class osoba char *imie; char *nazwisko; int wiek; osoba(char *i,char *n,int w); ~osoba() void drukuj(); ; /* klasa pochodna */ class student : public osoba char *wydzial; int semestr; student(char *i,char *n, int w,char *wy,int s); ~student() void drukuj(); void promocja(); ; Ford Fiat......... Skoda Rok akademicki 2016/2017, Wykład nr 5 19/40 Rok akademicki 2016/2017, Wykład nr 5 20/40 Dziedziczenie Dziedziczenie - sposoby dziedziczenia sposób dziedziczenia klasa podstawowa private protected public private - - - protected private protected protected public private protected public w klasie pochodnej można zdefiniować: dodatkowe dane składowe dodatkowe funkcje składowe dane i funkcje o takich samych nazwach jak w klasie podstawowej (dane i funkcje z klasy podstawowej są zasłaniane) jeśli nie podamy sposobu dziedziczenia, to domyślnie będzie to private podczas dziedziczenia nie są dziedziczone: konstruktor, destruktor i operator przypisania "=" Przykład: student st1("jan","kos",20,"we",2); st1.drukuj(); st1.osoba::drukuj(); - deklaracja obiektu - wywołanie funkcji z klasy student - wywołanie funkcji z klasy osoba możliwe jest dziedziczenie wielokrotne, tzn. klasa pochodna może być klasą podstawową dla innej klasy
Rok akademicki 2016/2017, Wykład nr 5 21/40 Rok akademicki 2016/2017, Wykład nr 5 22/40 Przykład nr 2 - dziedziczenie (1/4) Przykład nr 2 - dziedziczenie (2/4) #include <iostream> #include <cstring> using namespace std; class osoba private: char *imie; char *nazwisko; int wiek; osoba(char*,char*,int); ~osoba(); void drukuj(); ; klasa podstawowa (bazowa) class student : public osoba private: char *wydzial; int semestr; student(char*,char*,int, char*,int); ~student(); void drukuj(); void promocja(); ; klasa pochodna osoba::osoba(char *i, char *n, int w) imie = new char[strlen(i)+1]; nazwisko = new char[strlen(n)+1]; strcpy(imie,i); strcpy(nazwisko,n); wiek = w; osoba::~osoba() delete [] imie; delete [] nazwisko; void osoba::drukuj() cout << imie << " " << nazwisko; cout << " " << wiek << endl; destruktor klasy osoba konstruktor klasy osoba Rok akademicki 2016/2017, Wykład nr 5 23/40 Rok akademicki 2016/2017, Wykład nr 5 24/40 Przykład nr 2 - dziedziczenie (3/4) Przykład nr 2 - dziedziczenie (4/4) student::student(char *i,char *n,int w,char *wy,int s) : osoba(i,n,w) wydzial = new char[strlen(wy)+1]; strcpy(wydzial,wy); semestr = s; student::~student() delete [] wydzial; konstruktor klasy student void student::drukuj() osoba::drukuj(); cout << "Wydzial: " << wydzial; cout << " Semestr: " << semestr << endl; destruktor klasy student lista inicjalizacyjna konstruktora klasy student zawierająca wywołanie konstruktora klasy podstawowej (osoba) void student::promocja() semestr++; int main() student st1("jan","kowalski",20,"we",2); st1.drukuj(); st1.promocja(); st1.drukuj(); st1.osoba::drukuj(); jako pierwszy zostanie wywołany konstruktor klasy podstawowej (osoba) a po nim konstruktor klasy pochodnej (student) kolejność wywołania destruktorów jest odwrotna w stosunku do konstruktorów - jako pierwszy jest wywoływany destruktor klasy student, a po nim destruktor klasy osoba
Rok akademicki 2016/2017, Wykład nr 5 25/40 Rok akademicki 2016/2017, Wykład nr 5 26/40 Przykład nr 2 - dziedziczenie (4/4) void student::promocja() Jan Kowalski 20 jako pierwszy Wydzial: zostanie wywołany WE Semestr: 2 semestr++; konstruktor Jan klasy Kowalski podstawowej 20 (osoba) a po Wydzial: nim konstruktor WE Semestr: klasy 3 pochodnej Jan Kowalski (student) 20 int main() student st1("jan","kowalski",20,"we",2); st1.drukuj(); st1.promocja(); st1.drukuj(); st1.osoba::drukuj(); kolejność wywołania destruktorów jest odwrotna w stosunku do konstruktorów - jako pierwszy jest wywoływany destruktor klasy student, a po nim destruktor klasy osoba Funkcje wirtualne (polimorfizm) Przykład: załóżmy, że piszemy program wyświetlający na ekranie różne figury (kwadrat, trójkąt, koło) do wyświetlenia każdej figury stosowana jest oddzielna funkcja, figury powinny być wyświetlane na ekranie w określonej kolejności Problem: Rozwiązanie: jak zorganizować przechowywanie informacji o figurach? jak zorganizować wyświetlanie figur? klasy + dziedziczenie + funkcje wirtualne definiujemy klasę podstawową (figura) oraz trzy klasy pochodne (kwadrat, trojkat, kolo) w klasie podstawowej umieszczamy funkcję void rysuj() poprzedzoną słowem virtual (funkcja ta nic nie robi) w klasach pochodnych umieszczamy funkcje o takich samych nazwach jak w klasie podstawowej - void rysuj() wyświetlające poszczególne figury Rok akademicki 2016/2017, Wykład nr 5 27/40 Rok akademicki 2016/2017, Wykład nr 5 28/40 Przykład nr 3 - funkcje wirtualne (1/3) Przykład nr 3 - funkcje wirtualne (2/3) #include <iostream> using namespace std; class figura virtual void rysuj() ; ; klasa podstawowa figura funkcja wirtualna rysuj() class kwadrat : public figura klasa pochodna kwadrat void rysuj() cout << "Kwadrat" << endl; ; class trojkat : public figura klasa pochodna trojkat void rysuj() cout << "Trojkat" << endl; ; class kolo : public figura klasa pochodna kolo void rysuj() cout << "Kolo" << endl; ;
Rok akademicki 2016/2017, Wykład nr 5 29/40 Rok akademicki 2016/2017, Wykład nr 5 30/40 Funkcje wirtualne (polimorfizm) Przykład nr 3 - funkcje wirtualne (3/3) jeśli wskaźnikowi do klasy podstawowej (figura) przypiszemy adres obiektu klasy pochodnej (kwadrat, trojkat, kolo), to wywołując poprzez wskaźnik funkcję rysuj(), wywołamy funkcję odpowiadającą danemu obiektowi, np. figura *ptr; kwadrat kw1; trojkat tr1; kolo kol1; ptr = &kw1; ptr->rysuj(); ptr = &tr1; ptr->rysuj(); ptr = &kol1; ptr->rysuj(); - deklaracja wskaźnika do obiektu klasy figura - deklaracja obiektu klasy kwadrat - deklaracja obiektu klasy trojkat - deklaracja obiektu klasy kolo - wywołana zostanie funkcja rysuj() z klasy kwadrat - wywołana zostanie funkcja rysuj() z klasy trojkat - wywołana zostanie funkcja rysuj() z klasy kolo int main() kwadrat kwadrat1, kwadrat2; trojkat trojkat1, trojkat2; kolo kolo1, kolo2; figura *lista[6]; lista[0] = &trojkat1; lista[1] = &kwadrat1; lista[2] = &kolo1; lista[3] = &kwadrat2; lista[4] = &kolo2; lista[5] = &trojkat2; for (int i=0; i<6; i++) lista[i]->rysuj(); Trojkat Kwadrat Kolo Kwadrat Kolo Trojkat mówimy, że w powyższym przykładzie wystąpił polimorfizm (wielopostaciowość) Rok akademicki 2016/2017, Wykład nr 5 31/40 Rok akademicki 2016/2017, Wykład nr 5 32/40 System operacyjny - definicja System operacyjny - definicja System operacyjny - jest to program sterujący wykonywaniem aplikacji i działający jako interfejs pomiędzy aplikacjami (użytkownikiem) a sprzętem komputerowym użytkownik końcowy nie jest zainteresowany sprzętem, interesują go tylko aplikacje (programy użytkowe) aplikacje są tworzone przez programistów za pomocą języków programowania System operacyjny - administrator zasobów - zarządza i przydziela zasoby systemu komputerowego oraz steruje wykonaniem programu zasób systemu - każdy element systemu, który może być przydzielony innej części systemu lub oprogramowaniu aplikacyjnemu do zasobów systemu zalicza się: czas procesora pamięć operacyjną urządzenia zewnętrzne
Rok akademicki 2016/2017, Wykład nr 5 33/40 Rok akademicki 2016/2017, Wykład nr 5 34/40 Zarządzanie procesami Blok kontrolny procesu Głównym zadaniem systemu operacyjnego jest zarządzanie procesami Definicja procesu: proces - program w trakcie wykonania proces - ciąg wykonań instrukcji wyznaczanych kolejnymi wartościami licznika rozkazów wynikających z wykonywanej procedury (programu) proces - jednostka, którą można przypisać procesorowi i wykonać Proces składa się z kilku elementów: kod programu dane potrzebne programowi (zmienne, przestrzeń robocza, bufory) kontekst wykonywanego programu (stan procesu) - dane wewnętrzne, dzięki którym system operacyjny może nadzorować proces i nim sterować struktura danych tworzona i zarządzana przez system operacyjny, a opisująca właściwości procesu identyfikator - unikatowy numer skojarzony z procesem, dzięki któremu można odróżnić go od innych procesów stan procesu: nowy, gotowy, uruchomiony, zablokowany, anulowany priorytet - niski, normalny, wysoki, czasu rzeczywistego licznik programu - adres kolejnego rozkazu w programie, który ma zostać wykonany wskaźniki pamięci - wskaźniki do kodu programu, danych skojarzonych z procesem, dodatkowych bloków pamięci dane kontekstowe - dane znajdujące się w rejestrach procesora, gdy proces jest wykonywany informacje na temat stanu żądań we-wy - informacje na temat urządzeń we-wy przypisanych do tego procesu Rok akademicki 2016/2017, Wykład nr 5 35/40 Rok akademicki 2016/2017, Wykład nr 5 36/40 Dwustanowy model procesu Dwustanowy model procesu najprostszy model polega na tym, że w dowolnej chwili proces jest wykonywany przez procesor (uruchomiony) lub nie (nie uruchomiony) procesy, które nie są uruchomione czekają w kolejce na wykonanie Program przydzielający Wejście Proces nie uruchomiony Uruchomiony proces Wyjście Przerwa system operacyjny tworząc nowy proces, tworzy blok kontrolny procesu po czym wprowadza proces do systemu jako nie uruchomiony jeśli wykonywanie procesu zostało anulowane lub zakończone, to opuszcza on system, a program przydzielający wybiera kolejny proces z kolejki, który zostanie wykonany w pewnym momencie aktualnie wykonywany proces zostaje przerwany i program przydzielający wybiera inny proces do wykonania stan poprzednio uruchomionego procesu jest zmieniany z uruchomionego na nie uruchomiony
Rok akademicki 2016/2017, Wykład nr 5 37/40 Rok akademicki 2016/2017, Wykład nr 5 38/40 Pięciostanowy model procesu Pięciostanowy model procesu w dwustanowym modelu procesu kolejka działa na zasadzie FIFO, a procesor wykonuje procesy cyklicznie z kolejki problem pojawia się w przypadku, gdy kolejny proces pobierany do wykonania z kolejki jest zablokowany, gdyż oczekuje na zakończenie operacji we-wy rozwiązaniem powyższego problemu jest podział procesów nieuruchomionych na gotowe do wykonania i zablokowane uruchomiony - proces aktualnie wykonywany gotowy - proces gotowy do wykonania przy najbliższej możliwej okazji zablokowany - proces oczekujący na zakończenie operacji we-wy nowy - proces, który właśnie został utworzony (ma utworzony blok kontrolny procesu, nie został jeszcze załadowany do pamięci), ale nie został jeszcze przyjęty do grupy procesów oczekujących na wykonanie anulowany - proces, który został wstrzymany lub anulowany z jakiegoś powodu Rok akademicki 2016/2017, Wykład nr 5 39/40 Rok akademicki 2016/2017, Wykład nr 5 40/40 Pięciostanowy model procesu Koniec wykładu nr 5 podział procesów nieuruchomionych na gotowe do wykonania i zablokowane wymaga zastosowania minimum dwóch kolejek Dziękuję za uwagę! (Następny wykład: 14.12.2016) (Sprawdzian!!!) gdy pojawia się zdarzenie system operacyjny musi przejrzeć kolejkę szukając procesów, który związane są z danym zdarzeniem w celu zapewnienia większej wydajności lepiej jest gdy dla każdego zdarzenia istnieje oddzielna kolejka