Wstęp do programowania obiektowego Wykład 2 1
CECHY I KONCEPCJA PROGRAMOWANIA OBIEKTOWEGO 2
Cechy programowania obiektowego Dla wielu problemów podejście obiektowe jest zgodne z rzeczywistością (łatwe do przyswojenia przez człowieka; dobrze odzwierciedlające sposób, w jaki ludzie myślą o świecie). PO nie daje żadnych nowych efektów w gotowym programie Zastosowanie PO nie jest widoczne dla użytkownika Zastosowanie PO może pogorszyć wydajność obliczeniową programu (w porównaniu do analogicznego programu strukturalnego) 3
C++ jest językiem zarówno obiektowym, jak i imperatywnym/strukturalnym/proceduralnym. Języki programowania z cechami obiektowości to m.in.: C#, Java, Object Pascal, Lisp, Ruby, Python, PHP5. 4
Koncepcja programowania obiektowego Otaczający nas świat to różnego rodzaju przedmioty. Tworząc program komputerowy dokonujemy pewnego ich odwzorowania. W programowaniu proceduralnym funkcje i zmienne dotyczące danego przedmiotu nie są ze sobą logicznie powiązane. W programowaniu obiektowym dokonuje się powiązania metod (funkcji programu) z danymi (zmiennymi) definiującymi przedmiot. Wszystko to grupuje się w klasie zawierającej w jednym miejscu zarówno dane definiowanego przedmiotu, jak i dotyczące go funkcje. 5
Podstawowe definicje Klasa (częściowa lub całkowita) definicja dla obiektu - opis budowy i działania obiektów Obiekt fizyczna instancja (ucieleśnienie, egzemplarz) klasy. Jest to struktura zawierająca: dane metody, czyli funkcje służące do wykonywania na tych danych określonych zadań. Każdy obiekt ma trzy cechy: tożsamość, czyli cechę umożliwiającą jego identyfikację i odróżnienie od innych obiektów; stan, czyli aktualny stan własnych danych; zachowanie, czyli zestaw metod wykonujących operacje na tych danych. 6
UKRYWANIE IMPLEMENTACJI: HERMETYZACJA (ENKAPSULACJA) 7
Hermetyzacja Zwykłemu użytkownikowi niepotrzebna jest znajomość wewnętrznej budowy urządzeń. Do codziennej obsługi, wystarczą nam udostępnione bezpieczne manipulatory. Mamy dostęp tylko do wybranych metod urządzenia (np. manipulatory telewizora). W ten sposób część (bardziej skomplikowanych) funkcji jest przed nami ukryta. Analogicznie jest w programowaniu obiektowym: Hermetyzacja (enkapsulacja) - ograniczenie bezpośredniego dostępu do niektórych pól obiektów, ewentualnie umożliwienie ich modyfikacji poprzez metody. 8
Specyfikatory (etykiety) dostępu O dostępności pól i metod decydują tzw. specyfikatory dostępu: public, private i protected. W C++ w ciele klasy deklaruje się sekcje (zawierające jedną lub więcej składowych): public składowe widoczne globalnie, dla wszystkich, np. z maina; private składowe widoczne tylko dla danej klasy; protected składowe widoczne tylko dla danej klasy oraz klas dziedziczących. class Punkt { }; public: int x, y; double odległość (int xx, int yy) {return sqrt((x-xx)*(xxx)+(y-yy)*(y-yy));} private: void zeruj () {x = 0; y = 0;} protected: double przesuń (int dx, int dy) {x+=dx; y+=dy;} 9
Etykieta dotyczy występujących po niej metod i pól. Liczba etykiet jest dowolna (mogą się powtarzać). W przypadku braku etykiety domyślnie jest przypisana sekcja prywatna. class Punkt{ void fun1(){ } //prywatna, bo domyślna protected: int fun2(){ } int y; private: float fun3(){ } int x; public: double fun4(){ } }; 10
Konwencja nazewnictwa get/set/is Kiedy pole jest zadeklarowane jako prywatne możemy umożliwić : odczyt wartości pola przez metodę publiczną nazywaną get<nazwa pola>(). modyfikację wartości pola przez metodę publiczną nazywaną set<nazwa pola>. class Punkt { private: int x; int y; public: int getx(){return x;} int gety(){return y;} void setx(int xx){x=xx;} void sety(int yy){ if (yy>=0) y=yy;} }; 11
W C++ jest to tylko konwencja nazewnictwa (zalecany sposób tworzenia nazw metod do odczytu i zapisu pól prywatnych), a nie obowiązkowy element języka. Stosuje się to w wielu językach obiektowych, niekiedy jest to też mechanizm wbudowany (np. akcesory set, get w C#). 12
Korzyści z hermetyzacji Funkcje getx(), gety(), setx() i sety() znajdują się w sekcji public i są widoczne poza obiektem klasy. Pola x i y nie są widoczne poza obiektem klasy, ale jedynie wewnątrz obiektu (mają do niej dostęp metody klasy, czyli m.in. nasze funkcje getx(), gety(), setx() i sety() ). Z tego względu te funkcje stanowią "interfejs" dostępu do pól obiektu klasy Punkt. Daje to nam kontrolę nad zapisem i odczytem do pól x i y poprzez odpowiednie definicje metod (możemy sprawdzić, odrzucić czy zabronić modyfikacji pola). Standardowe nazwy takich metod ułatwiają czytanie kodu. 13
Dwie role programistów PO Twórca klas tworzy biblioteki kodu do wykorzystania, np. klasy, ich metody i dane Programista-klient używa stworzonego kodu, czyli gotowych klas: tworzy obiekty, wywołuje metody itp. Dobrze jest ukryć przed programistą-klientem nieistotne dla niego szczegóły implementacji, które mogłyby być niewłaściwie użyte. W ten sposób zmniejsza się ryzyko popełnienia przez niego błędów w używaniu gotowych bibliotek (klas). Takie postępowanie to właśnie ukrywanie implementacji (hermetyzacja). 14
Modyfikacje bibliotek Ukrywanie implementacji pozwala też na modyfikację przez twórcę klas szczegółów udostępnianych bibliotek, bez większego wpływu na kod wykorzystujący te biblioteki (kod napisany przez programistę-klienta). Modyfikacje to na przykład rozszerzanie funkcjonalności biblioteki czy optymalizacja kodu. 15
KONSTRUKTORY I DESTRUKTORY 16
Geneza Przyczyną wielu błędów jest to, że programista zapomina o inicjalizacji zmiennych lub "końcowych porządkach". W programowaniu obiektowym wprowadzono więc półautomatyczny mechanizm inicjacji i "sprzątania": odpowiednio: konstruktory i destruktory. 17
Konstruktor Przy tworzeniu obiektu zawsze wywoływany jest konstruktor (specjalna funkcja składowa klasy). Podstawowym zadaniem konstruktora jest inicjalizacja (nadanie odpowiednich wartości) polom tworzonego obiektu. 18
Cechy konstruktora: Nazwa konstruktora jest taka sama jak nazwa klasy; nie zwraca żadnego wyniku; może przyjmować parametry; Parametrami konstruktora nie mogą być obiekty klasy do której należy, ale mogą być referencje do tych obiektów (w C++); Nie może być wywołany jak zwykła metoda (bez tworzenia obiektu); Najczęściej jest publiczny. 19
Dwa sposoby inicjalizacji pól w C++ 1. W konstruktorze przy użyciu instrukcji przypisania, np.: Punkt(int xx, int yy) { x = xx; y = yy; } 2. W konstruktorze, przy użyciu listy inicjacyjnej, np.: Punkt(int xx, int yy) : x(xx), y(yy) {} Dopuszczalne są też rozwiązania mieszane. 20
Konstruktor domyślny W momencie tworzenia obiektu zawsze wywoływany jest jakiś konstruktor. Jeżeli w klasie nie został zdefiniowany żaden konstruktor, to kompilator dodaje (niewidoczny w kodzie) konstruktor domyślny (bezparametrowy). 21
Konstruktor bezparametrowy Często też definiuje się własny konstruktor bezparametrowy. Konstruktor bezparametrowy najczęściej nadaje polom wartości domyślne. 22
Tworzenie obiektów z wykorzystaniem konstruktorów: Punkt p(20,40), p1, p2; Punkt* wskp1 = new Punkt(30, 35); Punkt* wskp2 = new Punkt(); Punkt* wskp3 = new Punkt; 23
Konstruktor kopiujący Zadaniem konstruktora kopiującego jest utworzenie nowego obiektu jako dokładnej kopii obiektu istniejącego. Nagłówek zwykle ma postać: <nazwa klasy>(const <nazwa klasy>&) Przykład: Prostokąt(const Prostokąt& p) Przy braku definicji konstruktora kopiującego, kompilator dodaje domyślny konstruktor kopiujący, który kopiuje pole po polu (jest to tzw. kopia płytka i niekiedy to nie wystarcza). 24
Destruktor W momencie niszczenia obiektu (np.: koniec zakresu widoczności obiektu lokalnego) wywoływany jest destruktor zdefiniowany w klasie lub domyślny. Celem definiowania destruktora może być np. zwolnienie zaalokowanej ręcznie pamięci, używanych zmiennych dynamicznych, itp. porządki. Definicja destruktora: ~<nazwa klasy>() { <deklaracje zmiennych> <instrukcje>, itp. } Cechy destruktora: nie ma parametrów, nie ma typu wyniku w klasie może wystąpić tylko jeden destruktor nie można go wywołać jak zwykłej metody 25
DIAGRAMY KLAS W NOTACJI UML 26
Schemat klasy w UML Nazwa Atrybuty (zmienne klasowe) Metody (funkcje) Żarówka - bool włączona # int moc + void włącz() + void wyłącz() Symbole modyfikatorów dostępu: - private + public # protected Aby używać typu bool należy dopisać na początku programu: #include <stdbool.h> 27
Przykłady klas (UML) Punkt (na płaszczyźnie) Punkt (w przestrzeni 3D) Telewizor Człowiek Szafa/Pokój/Paczka Odcinek Wektor Wielokąt (budowany z punktów) Wielokąt (budowany z odcinków) 28