1 / 20

Programowanie obiektowe III rok EiT

Programowanie obiektowe III rok EiT. dr inż. Jerzy Kotowski Wykład XIII. Program wykładu. Język C++ Dziedziczenie Przykład problemu Podstawy języka JAVA Klasówka. Literatura. C++ for C programmers , Ira Pohl , The Benjamin/Cummings Publishing Company, Inc.

morse
Download Presentation

Programowanie obiektowe III rok EiT

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Programowanie obiektoweIII rok EiT dr inż. Jerzy Kotowski Wykład XIII

  2. Program wykładu • Język C++ • Dziedziczenie • Przykład problemu • Podstawy języka JAVA • Klasówka

  3. Literatura • C++ for C programmers, Ira Pohl, The Benjamin/Cummings Publishing Company, Inc. • Ćwiczenia z języka C++,Claude Delannoy • Symfonia C++, Jerzy Grębosz, Oficyna Kallimach, Kraków 1999

  4. Dziedziczenie – terminologia i podstawy • Inherit -dziedziczyć • Inheritance –dziedziczenie • Derive – pochodzić • Deriving– pochodzący • Derived– pochodny • A derivedclass– klasa pochodna • Virtual- czynny, prawdziwy, faktyczny • Pojęcie dziedziczenia należy do podstaw programowania obiektowego. • Dziedziczenie jest mechanizmem wyprowadzania nowej klasy ze starej. • Dziedziczenie umożliwia zdefiniowanie nowej klasy B, nazywanej pochodną, na podstawie już istniejącej klasy A, nazywanej bazową. • Wiele struktur to warianty jednej podstawowej struktury i często jest nudnym produkowanie tego samego kodu dla każdej klasy pochodnej z osobna.

  5. Dziedziczenie – podstawowe idee • Klasa pochodna dziedziczy opis podstawowej klasy. Może on być następnie zmieniony przez dodawanie nowych składników, polimorfizm istniejących już member functions i modyfikowanie przywilejów związanych z regułami dostępu. • Idea: Klasyfikacja bazowa z reguły obejmuje rozległe obszary wiedzy. Przykład: znając definicję ssaka i wiedząc, że zarówno mysz jak i słoń to ssaki można stworzyć ich opis w sposób dużo bardziej zwięzły niż przy wykorzystywaniu innych technik. • Virtual member functions – funkcje wirtualne. • Są to funkcje deklarowane w klasie bazowej i overloadedw klasach pochodnych. • Hierarchia ADT, definiowana przez proces dziedziczenia tworzy relację w zbiorze typów zdefiniowanych przez użytkownika. • Obiekty typów pochodnych mogą być wskazywane przez wskaźnik do klasy bazowej. Pozwala to na dynamiczne rozróżnianie typów. Jest to typowa cecha OOP. • Czyste funkcje wirtualne  klasy abstrakcyjne.

  6. Klasa pochodna • Klasa może pochodzić (can be derived) od klasy istniejącej przy pomocy konstrukcji:class class_name: (public|private) base_class {…} • public|privatesą opcjonalne. • Jak zwykle słowo kluczowe class może być zastąpione przez słowo kluczowe struct. Wtedy wszystkie składniki klasy są przez domniemaniepublic. • Najbardziej skomplikowanym aspektem klas pochodnych jest widzialność ich dziedziczonych składników. • Jedno z trzech słów kluczowych public, private, protected może być opcjonalnie wykorzystane do wyspecyfikowania jak składniki klasy bazowej mają być dostępne w klasie pochodnej. • Słowo kluczowe publicpo dwukropku w nagłówku klasy pochodnej oznacza, że wszystkie publicmembersklasy bazowej są publicmembersklasy pochodnej. • Klasa pochodna może być restrykcyjna. Może zmniejszać widzialność swoich składników i/lub zmieniać ich znaczenie.

  7. Modyfikatory widzialności • Słowa kluczowe public, private i protectedmogą być wykorzystane jako modyfikatory widzialności składników klasy. • A public memberjest widzialny w obszarze dostępności całego obiektu. • A private memberjest widzialny przez wszystkie funkcje składowewewnątrz swojej klasy. • A protected memberjest widzialny przez wszystkie funkcje składowe wewnątrz swojej klasy oraz przez funkcje składoweklasy bezpośrednio dziedziczącej. • Modyfikatory widzialności mogą być używane wewnątrz klasy w dowolnej kolejności i dowolną liczbę razy. • Modyfikatory widzialności mogą być wykorzystywane w nagłówku klasy pochodnej do ograniczenia widzialności elementów klasy bazowej. • W razie potrzeby, dla wybranych elementów klasy bazowej rodzaj widzialności można zmieniać indywidualnie wewnątrz klasy pochodnej.

  8. Dostęp do składowych klasy bazowej • W przypadku dziedziczenia publicznego wszystkie składowe publiczne klasy bazowej stają się składowymi publicznymi klasy pochodnej. • W przypadku dziedziczenia prywatnego, składowe publiczne klasy bazowej stają się składowymi prywatnymi klasy pochodnej. Użytkownik klasy pochodnej nie ma dostępu do składowych prywatnych klasy bazowej. • W obu typach dziedziczenia składowe prywatne klasy bazowej nie są udostępniane funkcjom składowym klasy pochodnej. • Jest to OCZYWISTE: Mechanizm dziedziczenia nie może łamać reguł dostępu wprowadzonych przez twórcę klasy bazowej.

  9. Dostęp do składowych klasy bazowej, c.d. • Dziedziczenie prywatne jest stosunkowo rzadkie i używane w szczególnych przypadkach (całkowita zmiana interfejsu klasy). • Składowe klasy bazowej mogą być chronione (protected). W tym przypadku są one uważane za: • publiczne dla funkcji składowych klasy pochodnej, • prywatne dla użytkownika klasy pochodnej. • Nawet jeżeli jakaś składowa z klasy bazowej (dana lub funkcja) została przedefiniowana w klasie pochodnej, to funkcje składowe i użytkownik klasy pochodnej mogą w dalszym ciągu z niej korzystać jeżeli nie jest to zabronione. • Dostęp do przedefiniowanej składowej umożliwia operator zakresu ::

  10. Wywołanie konstruktorów i destruktorów • Niech B będzie klasą pochodną do klasy bazowej A. • Jeżeli B ma jakiś konstruktor, to utworzenie obiektu typu B powoduje zawsze niejawne wywołanie konstruktora. • W konstruktorze B powinno się przewidzieć argumenty dla konstruktora A, chyba że A nie ma konstruktora lub ma konstruktor bezargumentowy. • Argumenty dla konstruktora A są podawane jak w przykładzie:B(int x, int y, char kol): A(x,y); • Argumenty dla A mogą mieć postać wyrażeń. • Konstruktory i destruktory nie są dziedziczone. • Nie jest również dziedziczony operator przypisania (podstawiania) nawet jeżeli został przeciążony w klasie bazowej. • Uzasadnienie: Jeżeli dokonano takiego przeciążenia to znaczy, że czynność ta jest nietypowa i należy traktować ją ze szczególną uwagą również w klasie pochodnej.

  11. Przypadek konstruktora kopiującego • Jeżeli klasa B nie ma konstruktora kopiującego to jest wywoływany jej automatyczny konstruktor kopiujący. Działa on następująco: • wywołuje konstruktor kopiujący z A (automatyczny lub kopiujący) • inicjalizuje te dane składowe B, które nie pochodzą z A. • Jeżeli w klasie pochodnej został jawnie zdefiniowany konstruktor kopiujący to jego wywołanie spowoduje: • wywołanie konstruktora klasy bazowej wymienionej w nagłówku jak w przykładzie: B(B& b): A(b);wywołanie konstruktora kopiującego z A, do którego zostanie przekazana część B odziedziczona a A. • wywołanie konstruktora bezargumentowego (domniemanego), jeżeli w nagłówku nie został wymieniony żaden konstruktor. Jeżeli klasa bazowa nie ma konstruktora bezargumentowego to wystąpią błędy kompilacji.

  12. Zgodność obiektu klasy bazowej i obiektu klasy pochodnej • Niech B będzie klasą pochodną do klasy bazowej A. A a;obiekt typu bazowego B b;obiekt typu pochodnego A *wska;wskaźnik obiektu typu bazowego B *wskb;wskaźnik obiektu typu pochodnego • W języku C++ mają miejsce dwie poniższe niejawne konwersje: • Przypisanie a=b; jest legalne i polega na przekształceniu b do typu A, czyli rozważeniu w A tylko składowych należących także do A i przypisaniu wyniku do a. Jest przy tym wywoływany operator przypisania z A, przeciążony lub automatyczny. Przypisanie odwrotne, czyli b=a; jest zabronione. • Przypisanie wskaźnika do klasy pochodnej we wskaźnik do klasy bazowej. Można napisać przykładowo: wska=wskb;natomiast napisanie bezpośrednio wskb=wska; jest zabronione. Ewentualnie można użyć rzutu: wskb=(B *)wska;

  13. Dziedziczenie wielobazowe (wielokrotne) • Dziedziczenie wielobazowe umożliwia dziedziczenie po kilku klasach jak w przykładzie:class B: public A1, public A2 • Każde dziedziczenie może być publiczne lub prywatne. Wykorzystanie składowych każdej z klas bazowych jest podobne jak w przypadku zwykłego dziedziczenia. • W przypadku napisu:class B: public A1, A2dziedziczenie po klasie A2 będzie prywatne przez domniemanie. • Operator zakresu :: może być używany: • jeżeli chce się użyć składowej klasy bazowej, przedefiniowanej w klasie pochodnej, albo • gdy dwie klasy bazowe mają składową o tej samej nazwie i trzeba sprecyzować o którą chodzi.

  14. Wywołania konstruktorów i destruktorów • Utworzenie obiektu pociąga za sobą wywołania konstruktorów klas bazowych w takiej kolejności w jakiej są wymienione w deklaracji klasy pochodnej (od lewej do prawej) • W nagłówku konstruktora klasy pochodnej można wymienić informacje do przekazania każdemu z konstruktorów klas bazowych. Przykład:B(typ1 a1, typ2 a2, typ3 a3): A1(a1,a2), A2(a3) {} • Nie jest to obowiązkowe, gdy klasa bazowa ma konstruktor bezargumentowy (domniemany) lub gdy nie ma konstruktora. • Przypadek ogólny: klasa pochodna ma składowe, które same w sobie są obiektami innych klas, które też mogą być klasami pochodnymi. • Problem kolejności wywołania konstruktorów (J. Grębosz): Klasa uszanuje najpierw starszych, potem swoich gości, a dopiero na samym końcu zajmie się sobą. • Jeżeli dziadek miał swojego gościa, to ta sprawa zostanie załatwiona już przy konstrukcji dziadka (J. Grębosz).

  15. Przykład – klasa punkt_kolorowy1 .. \test0.sln • punkt_kolorowy1.cpp • Idea: tworzymy klasę pochodną ze znanej od dawna klasy punkt dodając nową składową o nazwie kolor. Klasa punkt jest znana. • Interesujące elementy programu: • punkt_kolorowy(int=0, int=0, int=0); • Definicja konstruktora klasy pochodnejpunkt_kolorowy::punkt_kolorowy(int xx, int yy, int kolor): punkt(xx,yy) { kol=kolor; } • Alternatywa:punkt_kolorowy::punkt_kolorowy(int xx, int yy, int kolor): punkt(xx,yy), kol(kolor){} • Wywołanie funkcji z klasy bazowej • Niebezpieczeństwo nieskończonej rekursji • Wywołania funkcji operator !() • Niejawne wywołanie destruktora klasy bazowej

  16. Klasy wirtualne • Przy kilkustopniowym dziedziczeniu może zajść sytuacja, w której dana klasa będzie dziedziczyć dwukrotnie po innej klasie. Przykład: class B: public Aclass C: public Aclass D: public B, public C • Składowe klasy A pojawiają się dwukrotnie w klasie D. Aby tego uniknąć trzeba w deklaracjach pochodnych klas B i C zadeklarować z atrybutem virtual klasę, której powielania chce się uniknąć. • Słowo virtual może być umieszczone zarówno po jak i przed modyfikatorem widzialności:class B: virtual public Aclass C: public virtual A • Konstruktory klas pochodnych muszą przekazywać argumenty konstruktorowi klasy wirtualnej w sposób jawny:D(arg dla D):B(arg dla B),C(arg dla C),A(arg dla A) • W konstruktorach B i C, w których zadeklarowano klasę wirtualną nie trzeba umieszczać wywołania konstruktora klasy wirtualnej A. • Konstruktor klasy wirtualnej jest zawsze wywoływany na początku.

  17. Przykład – amfibia.cpp.. \test0.sln • Idea: Klasa samochod i klasa boat dziedziczą po klasie pojazd. Klasa amfibia dziedziczy po obu klasach samochod i boat. • Klasa pojazd przechowuje nazwę typu pojazdu. Dlatego ma konstruktor z dynamiczną rezerwacją pamięci. Destruktor, nie powinien być dwa razy wywoływany. • Wymaga to odpowiedniej precyzji: • Klasy samochod i boat mają po dwa konstruktory – jeden dla tworzenia obiektu swojego typu a drugi na potrzeby dziedziczenia wielokrotnego. • Klasa pojazd ma konstruktor z domniemanymi argumentami – aby oszukać kompilator gdy analizuje konstruktor z mniejszą liczbą argumentów w klasie samochod i boat. • Funkcja nazwa w klasie pojazd pozwala na ominięcie ograniczeń na dostęp do pola s w klasach pochodnych. • Wywołania destruktorów w kodzie klienta.

  18. Funkcje wirtualne – sformułowanie problemu • Klasyka: Niezależnie od tego na co wskazuje wskaźnik to jest to zawsze wskaźnik do typu wynikającego z deklaracji wskaźnika. • Dlatego wska->fun(); jest zawsze wywołaniem składowej z klasy bazowej. • Statyczne rozpoznawanie typu – kompilator • Dynamiczne rozpoznawanie typu – program • Język C++ pozwala na dynamiczne rozpoznawanie typu z powodu omówionej wcześniej zgodności klasy bazowej z klasą pochodną. • Jeżeli w klasie bazowej zadeklarujemy funkcję wirtualną przy pomocy słowa kluczowego virtual, to wywołania tej funkcji i funkcji w klasach pochodnych będą wybierane wyłącznie w zależności od rzeczywistego typu obiektu. • Nazywa się to dynamicznym nadawaniem typu lub dynamicznym wiązaniem funkcji. class A { .... public: void fun(...); ..... } a; class B: public A { ..... public: void fun(....); ...... } b; ..... A *wska = &a; B *wskb = &b; wska = wskb;

  19. Funkcje wirtualne – reguły stosowania • Słowo kluczowe virtual jest używane tylko raz dla danej funkcji i nie powinno być używane dla funkcji przedefiniowywanych w klasach pochodnych. • Metoda zadeklarowana w klasie bazowej jako wirtualna nie musi być przedefiniowywana w klasach pochodnych. • Funkcja wirtualna może być przeciążona. • Każda funkcja przeciążona może być wirtualna ale nie musi. • Konstruktor nie może być wirtualny a destruktor może. • Wiązanie dynamiczne jest z oczywistych względów wykorzystywane tylko przy hierarchii klas (dziedziczeniu).

  20. Przykład – klasa punkt_kolorowy2.cpp .. \test0.sln • Tworzymy klasę pochodną z dwóch klas bazowych: klasy punkt oraz klasy o nazwie kolor. • Klasa bazowa punkt posiada funkcję wirtualną kto. Funkcja ta jest przeciążana w klasie pochodnej punkt_kol. • W klasie bazowej funkcja ktowyprowadza tekst: Jestem punktem. • W klasie pochodnej funkcja ktowyprowadza wpierw tekst: Jestem kolorowym punktem w kolorze:. • W następnej linii jest wyprowadzany kolor punktu. Operator zakresu nie jest obligatoryjny! • Funkcja kto jest wykorzystywana przez funkcję składową printklasy bazowej punkt. • Zgodnie z metodyką funkcji wirtualnych rodzaj użytej metody zależy od typu argumentu. Oznacza to, że efekt działania linii p.print(); zależy od typu p, to znaczy od tego, czy p jest obiektem typu bazowego punkt czy też typu pochodnego punkt_kol. • Dynamiczne rozpoznawanie typu widać najlepiej w trzech ostatnich liniach segmentu main.

More Related