Funkcje wirtualne. Wskaźniki do klas pochodnych są podstawą dla funkcji wirtualnych i polimorfizmu dynamicznego.

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

PARADYGMATY PROGRAMOWANIA Wykład 4

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

Wykład 9: Polimorfizm i klasy wirtualne

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

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

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

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

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) { zdefiniuje. Integer::operator=(ri);

Abstrakcyjny typ danych

Wykład 8: klasy cz. 4

Dziedziczenie. Ogólna postać dziedziczenia klas:

TEMAT : KLASY DZIEDZICZENIE

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Automatyczne tworzenie operatora = Integer2& operator=(const Integer& prawy) {

Szablony funkcji i klas (templates)

Szablony. Szablony funkcji

Wykład 5: Klasy cz. 3

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

Szablony klas, zastosowanie szablonów w programach

Wykład 5 Okna MDI i SDI, dziedziczenie

Dziedziczenie. Tomasz Borzyszkowski

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

Polimorfizm. dr Jarosław Skaruz

Dziedziczenie jednobazowe, poliformizm

Obsługa wyjątków. Język C++ WW12

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Rozdział 4 KLASY, OBIEKTY, METODY

Zaawansowane programowanie w języku C++ Programowanie obiektowe

Funkcje przeciążone, konstruktory kopiujące, argumenty domyślne

Podstawy Programowania Obiektowego

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

.NET Klasy, obiekty. ciąg dalszy

Język C++ Programowanie obiektowe

Dokumentacja do API Javy.

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

Programowanie obiektowe - 1.

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

Wykład 6: Dziedziczenie

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

Szablony funkcji i szablony klas

Instrukcja do pracowni specjalistycznej z przedmiotu. Obiektowe programowanie aplikacji

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

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Funkcja, argumenty funkcji

Wykład 4: Klasy i Metody

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Zaawansowane programowanie w języku C++ Funkcje uogólnione - wzorce

Aplikacje w środowisku Java

10. Programowanie obiektowe w PHP5

Wykład 9: Metody wirtualne i polimorfizm

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

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

Identyfikacje typu na etapie. wykonania (RTTI)

Wprowadzenie do szablonów szablony funkcji

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

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

Wprowadzenie do szablonów szablony funkcji

Materiały do zajęć VII

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

Programowanie II. Lista 3. Modyfikatory dostępu plik TKLientBanku.h

Interfejsy i klasy wewnętrzne

Kurs WWW. Paweł Rajba.

Klasy abstrakcyjne i interfejsy

Laboratorium nr 10. Temat: Funkcje cz.2.

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

Przeciążenie operatorów

Projektowanie obiektowe. Roman Simiński Polimorfizm

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

C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm POLIMORFIZM

Dziedziczenie. dr Jarosław Skaruz

Klasa dziedzicząca posiada wszystkie cechy klasy bazowej (plus swoje własne) dodawanie nowego kodu bez edycji (i ewentualnego wprowadzania

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Podstawy programowania III

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

Klasy abstrakcyjne, interfejsy i polimorfizm

Zaawansowane programowanie w C++ (PCP)

Języki i techniki programowania Ćwiczenia 4 Wzorce

Wstęp do programowania obiektowego, wykład 7

Zaawansowane programowanie w C++ (PCP)

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

Programowanie w Internecie. Java

Podstawy Programowania semestr drugi. Wykład czternasty

Zaawansowane programowanie w C++ (PCP)

Programowanie obiektowe w języku

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

Funkcje. Spotkanie 5. Tworzenie i używanie funkcji. Przekazywanie argumentów do funkcji. Domyślne wartości argumentów

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

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

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

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

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

Technologie i usługi internetowe cz. 2

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

Programowanie obiektowe

PARADYGMATY PROGRAMOWANIA Wykład 2

Dziedziczenie jednobazowe, poliformizm, tablice wskaźników na obiekty

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

Transkrypt:

Funkcje wirtualne W C++ polimorfizm jest zrealizowany w dwa sposoby: na etapie kompilacji i na etapie wykonania. Na etapie kompilacji polimorfizm jest zrealizowany poprzez przeciążenie funkcji i operatorów. To jest polimorfizm statyczny. Na etapie wykonania (polimorfizm dynamiczny) polimorfizm jest zrealizowany poprzez funkcje wirtualne. Wskaźniki do klas pochodnych są podstawą dla funkcji wirtualnych i polimorfizmu dynamicznego. Wskaźnik do klasy bazowej może występować jako wskaźnik do obiektu dowolnej klasy pochodnej Podany poniżej przykład ilustruje fragment poprawnego kodu. Błąd o niespójności typów się nie generuje, kiedy wskaźnik typu klasy bazowej wskazuje do obiektu klasy pochodnej. 1

class base.. }; class derived : public base. }; void main() base *ptr; base ob_base; derived ob_derived; ptr = &ob_base; //wskaźnik ptr wskazuje na obiekt klasy bazowej } ptr = &ob_derived; //wskaźnik wskazuje na obiekt klasy pochodnej 2

to: Jeśli wskaźnik typu klasy bazowej wskazuje do obiektu klasy pochodnej, o przez ten wskaźnik możemy mieć dostęp tylko do tych składowych klasy pochodnej, które dziedziczą się z klasy bazowej klasa bazowa nic nie wie o składowych, które powstali w klasie pochodnej o arytmetyka wskaźników jest skojarzona z typem danych, do którego ten wskaźnik pozostał zadeklarowany. Z tego wynika: derived ob_derived[10] base *ptr = &ob_derived[0]; ptr++; //nie wskazuje na następny obiekt klasy pochodnej! o Wskaźnik klasy pochodnej nie można wykorzystać dla dostępu do obiektów klasy bazowej 3

Funkcja wirtualna to składową zadeklarowana w klasie bazowej i zdefiniowana w klasie pochodnej. Aby utworzyć funkcję wirtualną, należy jej deklaracje w klasie bazowej poprzedzić kluczowym słowem virtual. Klasa pochodna, która dziedziczy z klasy bazowej funkcją wirtualną, definiuje ją zgodnie ze swoimi potrzebami. W klasie bazowej funkcje wirtualne definiują postać interfejsu. Każde (ponowne) definiowanie funkcji wirtualnej w klasie pochodnej wyznacza specyficzną realizacje tej funkcji dla podanej klasy pochodnej. W taki sposób powstaje konkretna metoda. Przy definiowani funkcji wirtualnej w klasie pochodnej kluczowe słowo virtual nie jest potrzebne. Funkcje wirtualne mogą być wywołane jako zwykłe funkcje metody klasy. Przecież polimorfizm dynamiczny występuje, jeśli funkcje wirtualne są wywoływane przez wskaźnik do typu klasy bazowej, który wskazuje na obiekt klasy pochodnej. 4

Gdy wskaźnik klasy bazowej jest wykorzystywany do wskazania obiektu, zawierającego funkcję wirtualną, C++ decyduje o wyborze wersji funkcji na podstawie typu obiektu wskazywanego przez ten wskaźnik. Wybór wersji odbywa się na etapie wykonywania programu będzie wywoływana ta realizacja funkcji wirtualnej, która należy obiektowi klasy, wskazanej przez wskaźnik. Przykład W12_1 Ponowne definiowanie funkcji wirtualnej w klasie pochodenej różni się od przeciążenia funkcji: o prototyp funkcji (nazwa, ilość i typy argumentów formalnych i typ wartości zwracanej), redefiniowanej w klasie pochodnej, musi dokładnie odpowiadać prototypowi, występującemu w klasie bazowej. (Przy przeciążeni funkcji każda realizacje się różni listą argumentów formalnych). Jeśli prototyp funkcji wirtualnej pozostanie zmieniony w klasie pochodnej, to taka funkcja będzie potraktowana jako funkcja przeciążona, a nie wirtualna. o funkcje wirtualne muszą być niestatycznymi składowymi klasy, do której należą (funkcji przeciążone nie). 5

o Wybór realizacji funkcji wirtualnych jest dokonywany przy wykonani programu (dla funkcji przeciążonych na etapie kompilacji). Konstruktory nie mogą być funkcjami wirtualnymi, a desktruktory mogą Przykład W12.4 Dla tego, żeby podkreślić różnice pomiędzy funkcjami wirtualnymi i funkcjami przeciążonymi, redefiniowanie funkcji wirtualnych jest określone jako przesłanianie (overriding). Funkcje wirtualne mogą być wywołane przez użycie referencji do klasy bazowej (przykład W12_1). 6

Funkcja wirtualna zachowuje atrybut virtual przy dziedziczeni. Oznacza to, że przy kolejnym dziedziczeni w kolejnej klasie pochodnej funkcja wirtualna może być także przesłoniętą. Przykład W12_2. Jeśli funkcja wirtualna nie pozostała przesłonięta w klasie pochodnej, wtedy obiekty tej klasy, odwołujące się do takiej funkcji, będą wykorzystywali funkcję, zdefiniowaną w klasie bazowej. Funkcje abstrakcyjne (czysto wirtualne funkcji pure virtual functions) Często się okazuje, że w klasie bazowej nie da się stworzyć przydatną definicje funkcji wirtualnej po prostu klasa bazowa nie dysponuje dostatnią informacją. Wtedy klasa bazowa zawiera tylko deklaracje (prototyp) funkcji wirtualnej bez jej definicji wyznacza interfejs: virtual typ_zwrac nazwa_funkcji (lista parametrów) = 0; Składnia = 0 informuje kompilator o to, że definicji funkcji nie istnieje w klasie bazowej. 7

Każda klasa pochodna powinna mieć swoją własną definicje każdej funkcji abstrakcyjnej wyznacza swoją własną realizacje. Klas, który zawiera chociażby jedną funkcje abstrakcyjną, nazywamy klasą abstrakcyjną (abstract class). Niewolno tworzyć obiektów klas abstrakcyjnych takie klasy nie są pełne. Przecież można tworzyć wskażniki do klas abstrakcyjnych i referencji. Ważnym stosowaniem klas abstrakcyjnych i funkcji wirtualnych jest użycie ich w bibliotekach klas. Na przykład, klasa abstrakcyjna Figura zawiera wspólne dane dla różnych płaskich obiektów geometrycznych, ale nic nie wie o szczegółach realizacji obliczeni pola, zapisu danych na dysk, odczyt z dysku, wyświetlani danych na monitorze. Ale jest możliwe rozszerzyć funkcjonalność tej klasy stworzyć klasę pochodną, i w niej zdefiniować te realizacje metod wirtualnych, które są potrzebne dla każdego podanego obiektu. 8

Wczesne wiązanie odnosi się do wydarzenia, które ma miejsce przy kompilacji (obiekt i wywołanie funkcji są powiązane na etapie kompilacji). Przykłady: wywołanie funkcji zwyczajnej, wywołanie funkcji i operatorów przeciążonych. Główną zaletą jest wysoka wydajność (dla tego że wszystkie informacje do wywołania funkcji są określane na etapie kompilacji). Wada niewielka elastyczność. Późnie wiązanie dotyczy funkcji, wywołanie których jest determinowane przy wykonani programu. Przykład funkcje wirtualne, wywołanie których odbywa się przez wskaźnik lub referencje do klasy bazowej, a decyzje o to, która realizacje pozostanie wywołana, będzie podjęta przy wykonani programu na podstawie typu wskazywanego obiektu. Główna zaleta elastyczność, która nadaje możliwość reagować na różne zdarzenia zachodzące dopiero w trakcie wykonania programu. Może wydłużyć czas wykonania (dla tego że wywołanie funkcji nie jest zdeterminowane aż do momentu jej wykonania). 9

Szablony Szablony nadają możliwość tworzenia ogólnych funkcji i ogólnych klas. W ogólnych funkcjach (klasach) typ danych, na których operuje funkcja (klasa), jest określany jako parametr. Oznacza to, że można utworzyć funkcje (klasę) przystosowaną do pracy z kilkoma typami danych bez jawnego oprogramowania nowej wersji funkcji (klasy) dla każdego z poszczególnych typów danych. Funkcje ogólne Definiuje zestaw operacji, przeznaczonych do wykonywania na różnych typach danych Typ danych jest przekazywany jako parametr Wielu algorytmów mają dokładnie taką samą logikę dla różnych typów danych. Na przykład: przeszukiwanie obiektu w tablice (struktury danych), sortowanie obiektów w tablice (strukturze danych), dodawanie obiektu do tablicy (do struktury danych), usunięcie obiektu z tablicy (z struktury danych). 10

Zastosowanie funkcji ogólnych (klas ogólnych) jest bardzo skuteczne, kiedy algorytmy przetwarzania danych nie zależą od typów danych. Wtedy można rozdzielić kod na procedury, które nie zależą od typu danych (odnoszą się do algorytmów), i na procedury, które odnoszą się do konkretnego typu danych (zależą od typu danych). Ta cześć kodu, która nie zależy od typu danych, może być skutecznie napisana w postaci funkcji ogólnych (klas ogólnych). Przy tworzeni funkcji ogólnej jest tworzona funkcja, która potrafi sama się przeciążyć. Dla tworzenia funkcji ogólnych posługując słowem kluczowym template. Przy tym powstaje szkielet funkcji, w który w trakcie kompilacji będą podstawione odpowiednie typy danych kompilator sam generuje każdą konkretną realizacje dla podstawionego typu danych template <class Ttype> typ_zwrac nazwa_funkcji(lista_parametrów) //ciało funkcji } 11

Ttype typ danych, wykorzystywany przez funkcje. Nazwa ta może być używana wewnątrz definicji funkcji. To i jest symbol szablon. Wszystkie działania ogólnego algorytmu przy jego realizacji w tej funkcji będą wykonywane nad tym symbolem, a podczas tworzenia specyficznej wersji funkcji kompilator zamieni ten symbol-szablon na rzeczywisty (podstawiony) typ danych. Słowo class może być zamienione na słowo typename : template <typename Ttype> typ_zwrac nazwa_funkcji(lista_parametrów) //ciało funkcji } Przykład: W13_0 Funkcje ogólna (definicja tej funkcji pozostała poprzedzoną kluczowym słowem template) jest także nazywana szablonem funkcji. Dla każdego podstawionego typu danych kompilator tworzy konkretną specjalizacje tej funkcji mówimy, że kompilator stworzył funkcje wygenerowaną (generated function). Proces generowania funkcji nazywają tworzeniem egzemplarza (instantiation). Słowo kluczowe template może być w innej linii: 12

template <class T> void findmydata(t ob, size_t len).. } Pomiędzy linią template <class T> i linią void findmydata. nie mogą się pojawić żadne instrukcje. W szablonie można wykorzystać więcej niż jeden typ ogólny: template <class T1, class T2> void myfunc(t1 a, T2 b).. } Przy tworzeni szablonu funkcji polecamy kompilatorowi wygenerowanie tyle różnych wersji tej funkcji, ile jest potrzebne do obsługi wszystkich różnych wywołań funkcji występujących w programie. Funkcje-szablony są podobne do funkcji przeciążonych, przecież są bardziej ograniczone. Przy przeciążeni funkcji można wykonywać dowolne zmiany w instrukcjach kodu, funkcja-szablon musi wykonywać te same 13

instrukcji kodu dla każdej wersji. Mimo że funkcja-szablon sama w miarze potrzeby dokonuje przeciążenie, jednak można wykonać przeciążenie jawne: template <class T> void swapargs(t &a, T &b) T tmp; tmp = a; // class T powinien mieć przeciążenie operatora = a = b; b = tmp; } //Tu funkcje-szablon pozostanie redefiniowaną void swapargs(double &a, double &b) double t = a; a = (a+b)*(a+b); b = t; } void main() double a = 10.0, b = 20.0; coord c1(0, 0), c2(1,1); swapargs(c1, c2); //wywołanie pierwotną swapargs swapargs(a, b); //wywołanie przeciążoną swapargs } 14

Podany przykład nie jest typowym. Jeśli logika programu jest taka, że każdą wersja funkcji powinna wykonywać swój własny algorytm, bardziej naturalnym jest użycie funkcji przeciążonych. Jeśli dla każdej wersji algorytm jest taki samy, a różnica występuje tylko w typie danych, naturalnie używać szablony. Klasy ogólne Klasa zawiera definicje wykorzystanych przez nią algorytmów, jednak typ danych, na których klasa operuje, zostanie przekazany do niej jako parametr dopiero w chwili tworzenia obiektu. Klasy ogólne są przydatne kiedy jeden zbiór algorytmów trzeba zastosowywać dla różnych typów danych. Deklaracja klasy-szablonu temlate <class Ttype> class nazwa_clasy }; Ttype nazwa typu, który zostanie określony przy tworzeni egzemplarza klasy. 15

Tworzenie egzemplarza klasy: nazwa_clasy <typ> obiekt; typ to ten typ danych, na którym ma operować klasa. Funkcje składowe klasy ogólnej automatycznie stają funkcjami ogólnymi, nie ma konieczności poprzedzać ich nazwę słowem kluczowym template przy deklaracji ich prototypów. Przykład: W14_0 Szablony klas nadają możliwość stworzyć ogólną postać algorytmu, który dalej można użyć dla obsługi danych dowolnego typu. To pozbawia programistę od napisania wielokrotnych realizacji tego samego algorytmu dla różnych typów danych. Klasa szablon może mieć wielu typów danych: template <class T1, class T2> class myclass. }; 16

W klasie-szablonie można wykorzystać domyślne typy danych, związanych z typami danych standardowych: tempalte <class T1, class T2 = int> class my_cl... }; void main() my_cl< coord, double > aa(..); //T1 coord; T2 double my_cl <coord> bb(..); //T1 coord; T2 int } Przykład 14_1. Słowo kluczowe typename ma dwa zastosowania: o służy zamiennikiem słowa class o poinformowanie kompilatora, że nazwa użyta w deklaracji szablonu dotyczy nazwy typu, a nie nazwy obiektu: typename X::Name someobj; X::Name jest traktowane jako nazwa typu. 17

Słowo kluczowe export może poprzedzić deklaracje template. Nadaje możliwość umieścić deklarowanie szablonu w jednym pliku, a definicje w drugim. UWAGA! Kompilator Visual Studio 2005 nie wspiera słowa kluczowego export. Dla programów złożonych trzeba umieszczać deklaracje i definicje szablonu w pliku *.h, a w tych plikach, gdzie trzeba ten szablon udostępnić, inkludować plik nagłówkowy. 18