Dziedziczenie w klasach

Podobne dokumenty
TEMAT : KLASY POLIMORFIZM

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

TEMAT : KLASY DZIEDZICZENIE

dr inż. Jarosław Forenc

Zaawansowane programowanie w języku C++ Programowanie obiektowe

Klasy w C++ Pierwsza gra

PARADYGMATY PROGRAMOWANIA Wykład 4

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Mechanizm dziedziczenia

Plik klasy. h deklaracje klas

Programowanie obiektowe i zdarzeniowe

PROE wykład 4 pozostałe operatory, forward declaration, dziedziczenie. dr inż. Jacek Naruniec

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Wprowadzenie do klas w C++ oraz biblioteki opengl

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Programowanie obiektowe

Programowanie obiektowe

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Języki i metody programowania Java Lab2 podejście obiektowe

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Wykład 5 Okna MDI i SDI, dziedziczenie

2.4 Dziedziczenie. 2.4 Dziedziczenie Przykłady programowania w C - kurs podstawowy

Zaawansowane programowanie w C++ (PCP)

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

- Narzędzie Windows Forms. - Przykładowe aplikacje. Wyższa Metody Szkoła programowania Techniczno Ekonomiczna 1 w Świdnicy

Podstawy programowania obiektowego

Programowanie obiektowe

Szablony klas, zastosowanie szablonów w programach

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Wskaźniki a tablice Wskaźniki i tablice są ze sobą w języku C++ ściśle związane. Aby się o tym przekonać wykonajmy cwiczenie.

> C++ dziedziczenie. Dane: Iwona Polak. Uniwersytet Śląski Instytut Informatyki

Dziedziczenie jednobazowe, poliformizm

Programowanie obiektowe w języku C++ Zarządzanie procesami. dr inż. Jarosław Forenc. Przeładowanie (przeciążanie) operatorów

Rozdział 4 KLASY, OBIEKTY, METODY

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Aplikacje w środowisku Java

Podstawy programowania, Poniedziałek , 8-10 Projekt, część 1

C++ Przeładowanie operatorów i wzorce w klasach

Wprowadzenie do programowanie obiektowego w języku C++

Wykład 8: klasy cz. 4

Programowanie obiektowe w języku

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Język Java część 2 (przykładowa aplikacja)

Mechanizm dziedziczenia

Przykładowa dostępna aplikacja w Visual Studio - krok po kroku

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

Zaawansowane programowanie w C++ (PCP)

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Programowanie komputerowe. Zajęcia 7

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Laboratorium nr 12. Temat: Struktury, klasy. Zakres laboratorium:

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

Laboratorium 1 - Programowanie proceduralne i obiektowe

Programowanie obiektowe Wykład 3. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21

Programowanie Obiektowe i C++

Dokumentacja do API Javy.

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Deklaracja struktury w C++

Programowanie obiektowe

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Podstawy programowania w języku C++ Zadania - dziedziczenie i polimorfizm

Informatyka II. Laboratorium Aplikacja okienkowa

Wykład V. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Programowanie w C++ Wykład 13. Katarzyna Grzelak. 4 czerwca K.Grzelak (Wykład 13) Programowanie w C++ 1 / 26

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Technologie i usługi internetowe cz. 2

Język Java część 2 (przykładowa aplikacja)

Języki Programowania. Prowadząca: dr inż. Hanna Zbroszczyk. tel: Konsultacje: piątek:

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Programowanie obiektowe w języku C++ dr inż. Jarosław Forenc

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

PARADYGMATY PROGRAMOWANIA Wykład 2

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

Dziedziczenie. Tomasz Borzyszkowski

Instrukcja do pracowni specjalistycznej z przedmiotu. Obiektowe programowanie aplikacji

Wykład 4: Klasy i Metody

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Wybieramy File->New->Project Wybieramy aplikację MFC->MFC Application jak na rysunku poniżej:

Klasy i obiekty cz II

Temat: Transformacje 3D

Programowanie 2. Język C++. Wykład 3.

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

Dziedziczenie. Ogólna postać dziedziczenia klas:

Programowanie Obiektowe i C++

Typy zmiennych proste i złożone. Programowanie komputerów. Tablica. Złożone typy zmiennych. Klasa. Struktura

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

Wykład 1. Program przedmiotu. Programowanie (język C++) Literatura. Program przedmiotu c.d.:

Wstęp do programowania obiektowego, wykład 7

Wykład 9: Polimorfizm i klasy wirtualne

Programowanie w C++ Wykład 14. Katarzyna Grzelak. 3 czerwca K.Grzelak (Wykład 14) Programowanie w C++ 1 / 27

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Wstęp do Programowania 2

Transkrypt:

Dziedziczenie w klasach Na ostatnich zajęciach zajmowaliśmy się operacją agregacji w klasach. Polegała ona na łączeniu klas. W wyniku naszych działań otrzymaliśmy program którego schemat UML można przedstawić następująco (schemat ten nie uwzględnia klasy okna oraz klasy openglview): Na dzisiejszych zajęciach chcemy rozbudować nasz program dodając kolejne figury geometryczne tj. koło oraz trójkąt. Możemy zbudować nasz program opierając się dalej na agregacji i wtedy będzie on wyglądał w następujący sposób:

Zastanówmy się jednak czy takie podejście jest słuszne? Przecież inne figury mają częściowo takie same pola i metody. Różnią się jedynie właściwościami (polami) takimi jak promień czy wierzchołek. Ponadto każda klasa ma rożną funkcję Rysuj(). Skoro mamy tyle powtarzających się pól i metod być może istnieje sposób, aby napisać je tylko raz, a następnie tylko rozszerzyć opis w zależności czy mamy do czynienia z kwadratem, kołem czy trójkątem. Taką możliwość daje nam dziedziczenie (generalizacja). Zasada jest prosta tworzymy klasę, która opisuje ogólne właściwości pewnego zbioru obiektów (np. ssak jak wiadomo każde ma oczy, nogi, głowę itp.) następnie na jej podstawie budujemy klasy ściśle związane z danym gatunkiem i rozbudowujemy klasę bazową o właściwości danego gatunku. Wróćmy do naszego przykładu i zbudujmy klasę bazową CFigura. Na jej podstawie stworzymy klasy opisujące kwadrat, koło oraz trójkąt. Schemat UML będzie wyglądał następująco: Jak widać na rysunku posiadamy ogólną klasę CFigura, która jest później wykorzystywana przy tworzeniu klas potomnych CTrojkat, CKolo i CKwadrat. Każda z funkcji potomnych posiada swoją własną metodę Rysuj(); Po co w takim przypadku funkcja Rysuj() w klasie CFigura? Wyjaśnienie i korzyści jakie wynikają z tak przyjętej konwencji zawarte jest w dalszej części instrukcji. Pierwszym krokiem jest dodanie klasy CFigura. Dokonujemy tego np. poprzez wybór prawym przyciskiem na nazwie projektu Add->Class. Teraz czeka nas przeniesieni pól i metod z klasy CKwadrat do klasy CFigura. Zgodnie z powyższym diagramem UML, do pliku Figura.h dodajemy publiczne metody:

public: figury virtual void Rysuj()=0;//rysuje figure void Ukryj();//ukrywa figure void Pokaz();//pokazuje figure void Przesun(int dx, int dy, int dz);//ustawia zmienna odpowiedzialna za przesuniecie figury void Obroc(double alfa, double beta, double gamma); //ustawia zmienna odpowiedzialna za obrot gdzie metoda Rysuj()opatrzona jest etykietą virtual co oznacza, że jej definicja nie znajduje się w klasie CFigura, lecz w klasie potomnej. To właśnie tutaj nasza klasa staje się polimorficzna. Właściwość tę wykorzystamy w odpowiednim momencie. Wróćmy jednak do pliku nagłówkowego klasy CFigura. Należy tu również dodać deklaracje pól, które są chronione i mogą być dziedziczone co oznacza, że musimy użyć etykiety protected: protected: int kolor[3]; //kolor figury bool hide; //okresla czy figura ma byc wyswietlana 1 - nie, 0 - tak double translacja[3]; // przechowuje wspolrzedne przesuniecia w trzech osiach double rotacja[3]; //przechowuje wspolrzedne obrotow w trzech osiach Teraz należy dodać definicje metod klasy CFigura do pliku Figura.cpp.: void CFigura::Przesun(int dx, int dy, int dz) //przesuniecie figury translacja[0]+=dx; translacja[1]+=dy; translacja[2]+=dz; void CFigura::Obroc(double alfa, double beta, double gamma) // obrot figury rotacja[0]+=alfa; rotacja[1]+=beta; rotacja[2]+=gamma; void CFigura::Ukryj() // wylaczenie wyswietlania figury hide=1; void CFigura::Pokaz() // wylaczenie wyswietlania figury hide=0; Mając załatwioną kwestię klasy CFigura, można przejść do kodu klasy Ckwadrat. Modyfikacji ulec musi zarówno kod pliku nagłówkowego jak i definicji metod. Plik nagłówkowy powinien wyglądać następująco: class CKwadrat: public CFigura public: CKwadrat(void); //domyslny konstruktor CKwadrat(int x1, int y1, int x2, int y2,double r, double g, double b); // konstruktor, który automatycznie przypisuje wartości wierzchołkom i ustala kolor kwadratu ~CKwadrat(void); //destruktor void Rysuj(void); // funkcja rysująca figurę

private: CPunkt wierzcholek[4]; // wspólrzędne wierzchołków ; Jak widać dodaliśmy etykietę public oraz nazwę klasy CFigura zaraz po nazwie klasy CKwadrat, oznacza to, że klasa CKwadrat dziedziczy pola oraz metody po klasie CFigura (jest klasą potomną/pochodną klasy CFigura). Należy tez pamiętać aby załączyć: #include "Figura.h" do pliku nagłówkowego klasy CKwadrat, natomiast w pliku nagłówkowym klasy CFigura należy dołączyć: #include "punkt.h" Uwaga! Należy tym samym usunąć dołącznie #include "punkt.h" z pliku nagłówkowego CKwadrat. Teraz należy przebudować projekt (Built->Rebulid Solution) i uruchomić program. Jeśli wszystko zostało poprawnie wykonane, uzyskamy na ekranie efekt z ostatnich zajęć. Oczywiście nam zależy dodatkowo na narysowaniiu koła oraz trókąta zatem dodajemy ich klasy. Najpierw koło. Dodajemy klasę i tutaj, co ciekwego, w wizardzie klas możemy wybrać zależnoć pomiędzy klasami tj. dziedzictwo z klasy CFigura (wystarczy wpisać nazwę klasy bazowej): Wybieramy Finish. W pliku nagłówkowym automatycznie generowny jest kod klasy wraz z dopiskiem o dziedziczeniu: #pragma once #include "Figura.h" class CKolo : public CFigura public: CKolo(void); ~CKolo(void); ;

Następnie dopisujemy do pliku nagłówkowego kod, analogicznie jak w klasie CKwadrat, czyli: public: void Rysuj(void); // funkcja rysująca figurę CKolo(double p, int x, int y, int z, double r, double g, double b); //przeładowany konstruktor (ppromień, x, y, z - pozycja środka okręgu) CPunkt srodek; // współrzędne środka double promien; // promień Ostatecznie definiujemy zadeklarowane funkcje dla koła: CKolo::CKolo(double p, int x, int y, int z, double r, double g, double b) //przeładowany konstruktor (r-promień, x,y, z - pozycja środka okręgu) promien=p; srodek.pozycja[0]=x; srodek.pozycja[1]=y; srodek.pozycja[2]=z; kolor[0]=r; kolor[1]=g; kolor[2]=b; for (int i=0;i<3;i++) translacja[i]=0; rotacja[i]=0; hide=0; void CKolo::Rysuj() // funkcja rysująca figurę double angle, vectorx, vectory, vectorx1, vectory1; glpushmatrix();// save matrix prior to transform, pozwala na zmianę pozycji i orientacji tylko jednego obiektu gltranslatef(translacja[0],translacja[1],translacja[2]); //kolejność wykonania operacji translacji i rotacji definiuje względem którego układu obracany jest obiekt glrotatef( rotacja[0], 1.0, 0.0, 0.0 );//rotate about the X axis glrotatef( rotacja[1], 0.0, 1.0, 0.0 );//rotate about the Y axis glrotatef( rotacja[2], 0.0, 0.0, 1.0 );//rotate about the Z axis if (!hide) glcolor3f(kolor[0],kolor[1],kolor[2]);//ustawienie koloru glbegin(gl_triangles); vectorx1=srodek.pozycja[0]; vectory1=srodek.pozycja[1]; for(int i=0;i<=360;i++)//rysujemy koło składające sie z 360 trójkątów angle=i/57.29577957795135; vectorx=srodek.pozycja[0]+(promien*sin(angle)); vectory=srodek.pozycja[1]+(promien*cos(angle)); glvertex3d(srodek.pozycja[0],srodek.pozycja[1],srodek.pozycja[2]); glvertex3d(vectorx1,vectory1,srodek.pozycja[2]); glvertex3d(vectorx,vectory,srodek.pozycja[2]); vectory1=vectory; vectorx1=vectorx; glend(); glpopmatrix();// restore matrix after transform, koniec przekszta3cen na wybranym obiekcie

Ponieważ w powyższej definicji używamy funkcji trygonometrycznych, dlatego musimy dodać plik nagłówkowy #include <math.h> do pliku nagłówkowego klasy CKolo. Teraz musmy dodać wywołanie naszego koła. Po pierwsze musimy stworzyć wskaźnik do koła, potem powołać do życia przy użyciu operatora new a ostatecznie należy wywołac metodę rysującą. Owczywiście obiekt typu CKolo będzie dynamiczny więc należy też usunąć go ze sceny przy użyciu operatora delete. Całość tych operacji można wykonać usuwając obiekt o nazwie kwadrat2, nie będzie bownien on już nam potrzebny. Teraz po kolei, najpierw zaczynamy od pliku nagłówkowego klasy openglview: #pragma once #include <gl\glut.h> // dodajemy bibliotekę glut do projektu #include "Kwadrat.h" #include "Kolo.h" class openglview public: CKwadrat * kwadrat; // kwadrat nr 1 CKolo * kolo; Następnie zmianiamy postać konstruktora i destrukora klasy openglview: openglview::openglview(void) kwadrat = new CKwadrat(-20,-20,20,20,1,1,0); kolo = new CKolo(20,30,30,0,0,0,1); openglview::~openglview(void) delete kwadrat; delete kolo; Oraz metody rysującej: void openglview::rysuj_figury(void) kwadrat->rysuj(); //rysuje obiekt kwadrat kolo->rysuj(); //rysuje obiekt koło Teraz kompilujemy projekt i dopalmy program, na ekranie powinniśmy zobaczyć koło oraz kwadrat:

Analogicznie należy dodać trójkąt, pamiętajć jednak, że trojkąt składa się z trzech wierzchołków, które trzeba zdefiniować. Mając już trzy figury na scenie możemy przejść do meritum naszych zajęć, czyli wykorzystania polimorfizmu klas. Wykorzystamy wcześniej wspomnianą właściwość wynikającą z zadeklarowania w klasie bazowej wirtualnej funkcji Rysuj(), a zdefiniowania jej w odpowiednich klasach potomnych. Dodajmy zatem do klasy openglview tablicę wskaźników do obiektów typu CFigura: CFigura * figury[3]; // tablica figur Usuwając tym samym nasze poprzednie figury: CKwadrat * kwadrat; CKolo * kolo; CTrojkat * trojkat; Oraz dołączamy odpowiednie pliki w openglview.h : #include "figura.h" #include "Kwadrat.h" #include "Trojkat.h" #include "Kolo.h" Następnie zainicjalizujemy odpowiednio nasze figury w konstruktorze klasy: figury[0] = new CKolo(20,30,30,0,1,0,0); figury[1] = new CKwadrat(-25,-25,25,25,0,1,0); figury[2] = new CTrojkat(-30,-20,-60,-20, -45, 0, 1, 1, 0); Podejrzane? Do tablicy wskaźników do obiektów typu CFigura wpisujemy wskaźniki do obiektów typu Ckolo, CKwadrat itd. Takie działanie jest dozwolone, ponieważ wszystkie te klasy są potomnymi klasy CFigura. Gdybyśmy próbowali wpisać: figury[0] = new CPunt(40,0,0); //błąd!!!! Bo CPunkt nie jest potomkiem CFigura Oczywiście musimy pamiętać o zwolnieniu pamięci w destruktorze: for (int i=1;i<3;i++) //usuwany wszystkie dynamiczne obiekty delete figury[i]; Teraz przejdźmy do rysowania obiektów w funkcji rysuj_figury(): for (int i=0;i<3;i++) figury[i]->rysuj(); Tutaj kolejna interesująca właściwość. Mimo, że w tablicy znajdują się wskaźniki do obiektów klasy CFigura, program rozpoznaje czy powinna zostać wywołana funkcja Rysuj() dla koła, dla kwadratu czy dla trójkąta. Na koniec zmieniamy definicje funkcji obsługującej klawiaturę: void openglview::keyboard(unsigned char key, int x, int y) if (key == 27) exit(0); // escape key is ascii 27 if (key == 97) figury[1]->przesun(10,0,0); //key a if (key == 115) figury[1]->przesun(0,10,0); //key s if (key == 100) figury[1]->przesun(0,0,10); //key d if (key == 122) figury[1]->przesun(-10,0,0); //key z if (key == 120) figury[1]->przesun(0,-10,0); //key x if (key == 99) figury[1]->przesun(0,0,-10); //key c if (key == 102) figury[1]->obroc(10,0,0); //key f if (key == 103) figury[1]->obroc(0,10,0); //key g if (key == 104) figury[1]->obroc(0,0,10); //key h if (key == 118) figury[1]->obroc(-10,0,0); //key v if (key == 98) figury[1]->obroc(0,-10,0); //key b

if (key == 110) figury[1]->obroc(0,0,-10); //key n //if (key == 111) figury[1]->ukryj(); //key o //if (key == 112) figury[1]->pokaz(); //key o Jaki mamy zysk z polimorfizmu? Np. taki, że teraz możemy spojrzeć na obiekt ze strony klasy bazowej, tym samym możemy zmienić przesunięcie czy obrót każdej z figur. Wystarczy, że teraz dodamy licznik figury np. numer jako składnik klasy openglview, a następnie zmianimy jego wartość po naciśnięciu odpowiedniego klawisza np.: if (key == 109) numer++; if(numer>2) numer=0; //key m W każdym innym miejscu zmianimy odniesienie do figury: if (key == 110) figury[numer]->obroc(0,0,-10); //key n Teraz możemy operować na każdej z figur wybierając zmianę na przycisku m Timer To ostatnie zadanie na dzisiaj. Na początku dodajmy funkcję odpowiedzialną za zmianę pozycji i orientacji wszystkich figur na scenie (animacja) w klasie openglview: Deklaracja: void animacja(void); Definicja: void openglview::animacja(void) //funkcje odpowiedzialna za zmianę pozycji i orientacji wszystkich figur na scenie Teraz wróćmy do pliku opengl1dlg.cpp i dodajmy funkcję globalną: void animacja(int value) gl.animacja(); gluttimerfunc(30,animacja,0); //ustawiamy funkcje timera po 30ms zostanie wywołana funkcja gl.animacja() z parametem 0 Następnie wskażmy, która funkcja odpowiada za timer (w funkcji OnBnClickedOk): gluttimerfunc(30,animacja,0); //ustawiamy funkcje timera Korzystanie z timera zamiast funkcji Sleep(...) nie blokuje funkcji obsługi klawiatury. Jest to wygodne i często wykorzystane. Sprawozdanie: 1. Co oznacza słowo virtual przy metodzie klasy i jakie są konsekwencje jego wykorzystania? 2. Jak można przejść pomiędzy figurami, używając jednego klawisza na klawiaturze? Podaj część kodu, która realizuje tę operację w naszym programie. 3. Dlaczego w pliku kwadrat.h nie dołączamy pliku nagłówkowego punkt.h? 4. W jakim celu zdeklarowana została metoda Rysuj() w klasie CFigura? Podaj konkretny przykład (z programu), którego wykonanie nie byłoby możliwe bez tej deklaracji. 5. Jak jest różnica pomiędzy agregacją a dziedziczeniem? Czy można stosować je zamiennie?