Transcript polimorfizm

KURS JĘZYKA C++
– WYKŁAD 6 (7.04.2014)
Polimorfizm
SPIS TREŚCI
Metody wirtualne
 Implementacja polimorfizmu
 Wczesne i późne wiązanie metod wirtualnych
 Klasy abstrakcyjne
 Klasa pair<>
 Klasa vector<>

SKŁADOWE FUNKCJE WIRTUALNE
Składowe funkcje wirtualne pozwalają na przedefiniowanie w
każdej klasie pochodnej funkcji składowych zadeklarowanych
w klasie bazowej.
 Poprzez funkcje wirtualne w programie zapewnione jest
wywołanie metody najlepiej odpowiadającej obiektowi.
 Składowe funkcje wirtualne należy opatrzyć deklaratorem
virtual (wewnątrz klasy).
 W definicji metody wirtualnej poza klasą nie używa się
deklaratora virtual.

SKŁADOWE FUNKCJE WIRTUALNE

Przykład deklaracji klas z metodami wirtualnymi i zwykłymi:
class bazowa
{
public:
void opis_zwykly ();
virtual void opis_wirtualny ();
};
class pochodna: public bazowa
{
public:
void opis_zwykly ();
virtual void opis_wirtualny ();
};
SKŁADOWE FUNKCJE WIRTUALNE

Przykład definicji metod wirtualnych i zwykłych:
void bazowa::opis_zwykly()
{ cout << "bazowa::opis_zwykly()" << endl; }
void bazowa::opis_wirtualny()
{ cout << "bazowa::opis_wirtualny()" << endl; }
void pochodna::opis_zwykly()
{ cout << "pochodna::opis_zwykly()" << endl; }
void pochodna::opis_wirtualny()
{ cout << "pochodna::opis_wirtualny()" << endl; }
SKŁADOWE FUNKCJE WIRTUALNE

Przykład użycia metod wirtualnych i zwykłych:
bazowa *a = new bazowa();
a->opis_zwykly();
a->opis_wirtualny();
// bazowa::opis_zwykly()
// bazowa::opis_wirtualny()
bazowa *b = new pochodna();
b->opis_zwykly();
b->opis_wirtualny();
// bazowa::opis_zwykly()
// pochodna::opis_wirtualny()
SKŁADOWE FUNKCJE WIRTUALNE
 Funkcja
wirtualna musi być zdefiniowana dla klasy, w
której po raz pierwszy została zadeklarowana.
 Funkcji wirtualnej można używać nawet wtedy, gdy z jej
klasy nie wyprowadzi się żadnej klasy pochodnej.
 Klasa pochodna, która nie potrzebuje specjalnej wersji
funkcji wirtualnej, nie musi jej dostarczać.
 Funkcja w klasie pochodnej z tą samą nazwą i z tą samą
listą argumentów co funkcja wirtualna w klasie
podstawowej nadpisuje (ang. override) wersję funkcji
wirtualnej z klasy bazowej.
POLIMORFIZM
 Uzyskanie
zachowania się funkcji adekwatnego do typu
obiektu nazywa się polimorfizmem (ang. polymorphism).
 Klasa z funkcjami wirtualnymi nazywa się klasą
polimorficzną.
 Aby zachowanie obiektu było polimorficzne należy się do
niego odnosić za pomocą wskaźnika albo referencji.
 Dzięki polimorfizmowi programy stają się rozszerzalne
(ang. extensibility) – modyfikacja kodu polega na dodaniu
nowej klasy bez potrzeby zmian w kodzie istniejącym.
IMPLEMENTACJA ZACHOWAŃ POLIMORFICZNYCH
 Obiekty
klas polimorficznych mają dodatkowe pole
identyfikujące typ obiektu.
 Decyzję o wyborze funkcji do wykonania podejmuje się w
trakcie działania programu (jest to tak zwane późne
wiązanie, w przeciwieństwie do zwykłych funkcji gdzie
obowiązuje wczesne wiązanie).
 Każda klasa polimorficzna posiada swoje miejsce w tablicy
metod wirtualnych.
 Polimorfizm jest więc kosztowny (miejsce i czas) – dlatego
nie wszystkie metody są wirtualne.
REZULTAT FUNKCJI WIRTUALNEJ

Przy nadpisywaniu funkcji wirtualnej trzeba zachować
odpowiedni typ rezultatu:
albo rezultat musi być identyczny,
 albo rezultat musi być kowariantny (referencja lub wskaźnik
do obiektu tej samej klasy lub do klasy, dla której jest ona
jednoznaczną i dostępną klasą podstawową).


Przykład:
owoc * bazowa::fun () {/*…*/}
pomelo * pochodna::fun () {/*…*/}
INNE CECHY FUNKCJI WIRTUALNYCH
Funkcja wirtualna w klasie nie może być statyczna.
 Dostęp do funkcji wirtualnej może być zmieniony w
klasach pochodnych (co zależy od sposobu
dziedziczenia) – dostęp ten zależy więc tylko od typu
wskaźnika albo referencji.
 Funkcje wirtualne mogą być przyjacielami w innych
klasach.

FUNKCJA WIRTUALNA
WCZEŚNIE ZWIĄZANA
 Funkcja
wirtualna będzie wcześnie związana gdy będzie
wywołana na rzecz konkretnego obiektu znanego z nazwy:
klasa ob;
// …
ob.funwirt();
 Funkcja wirtualna będzie wcześnie związana gdy użyjemy
kwalifikatora zakresu:
wsk->klasa::funwirt();
ref.klasa::funwirt();
 Funkcja wirtualna będzie wcześnie związana gdy wywołamy
ją w konstruktorze.
 Funkcja wirtualna może być wbudowana.
KLASY ABSTRAKCYJNE





Klasy abstrakcyjne służą do definiowania interfejsów (pojęć
abstrakcyjnych).
Klasa abstrakcyjna zawiera co najmniej jedną abstrakcyjną metodą
wirtualną (funkcja czysto wirtualna).
Deklaracja metody czysto wirtualnej wygląda następująco:
virtual typ funkcja (lista-argumentów) = 0;
Nie trzeba (ale można) podawać definicji metody czysto wirtualnej.
W klasach potomnych, które nie mają być klasami abstrakcyjnymi,
należy zdefiniować wszystkie odziedziczone metody abstrakcyjne.
KLASY ABSTRAKCYJNE



Nie wszystkie metody w klasie abstrakcyjnej muszą być abstrakcyjne.
Żaden konstruktor ani destruktor nie może być abstrakcyjny.
Nie można utworzyć obiektu klasy abstrakcyjnej:
 nie wolno zdefiniować funkcji, która odbierałaby argument takiej klasy
przez wartość;
 nie wolno zdefiniować funkcji, która zwracałaby wynik takiej klasy przez
wartość;
 klasa abstrakcyjna nie może być typem w jawnej konwersji.
WIRTUALNY DESTRUKTOR

W klasach polimorficznych (zawierających metody
wirtualne) destruktor definiujemy jako wirtualny.
KONSTRUKTOR NIE MOŻE BYĆ WIRTUALNY ALE…

Czasami istnieje potrzeba wyprodukowania nowego
obiektu tej samej klasy – w takiej sytuacji można
zdefiniować funkcję wirtualną, która będzie
przygotowywać taki obiekt (zastąpi konstruktor
domyślny albo kopiujący).
KLASA PAIR<>
W pliku nagłówkowym <utility> zdefiniowano szablon
klasy pair<F,S>, który służy do przechowywania pary
wartości.
 Para std::pair<> to struktura z dwoma polami first i
second dowolnych typów podawanych jako parametry
wzorca.
 Przykład:
pair<string,double> pi =
new pair<string,double>(”pi”,3.141593);
 W szablonie klasy std::pair<> zdefiniowano oprócz
konstruktora z dwiema wartościami inicjalizującymi pola
first i second także konstruktor kopiujący i domyślny.

KLASA PAIR<>
Typy występujące w parze mogą być wydedukowane, gdy para
jest tworzona za pomocą funkcji szablonowej make_pair().
 Przykład:
pair<string,double> e =
make_pair(string(”e”),2.718282);
 Dla szablonu klasy std::pair<> zdefiniowano nieskładowe
operatory relacji porównujących == i < (porównywane są
pierwsze pola, a gdy te są równe porównywane są drugie
pola).
 Funkcja składowa oraz zewnętrzna funkcja szablonowa
swap<>() zamienia zawartości dwóch par.

KLASA VECTOR<>



W pliku nagłówkowym <vector> zdefiniowano szablon klasy
vector<T>, który służy do przechowywania dowolnej liczby
wartości określonego typu.
Kolekcja std::vector<> przechowuje swoje elementy w tablicy
dynamicznej – dodawanie (metoda push_back()) i usuwanie
(metoda pop_back()) elementów na końcu tablicy działa bardzo
szybko.
Przykład:
vector<int> coll;
for (int i=1; i<=10; ++i)
coll.push_back(i*i);
for (int i=0; i<coll.size(); ++i)
cout << coll[i] << ’ ’;
cout << endl;
KLASA VECTOR<>

W kolekcji std::vector<> istnieją metody do wstawiania
elementu na początku wektora push_front() i usuwania
elementu z początku pop_front().

Dostęp do elementów w wektorze jest realizowany za pomocą
operatora indeksowania [] albo za pomocą metody at();
odczytanie wartości pierwszego elementu w wektorze można zrobić
za pomocą metody front() a ostatniego za pomocą back().
Metoda empty() mówi czy wektor jest pusty a metoda size()
zwraca liczbę wszystkich elementów w wektorze.
Metoda clear() usuwa wszystkie elementy z wektora.

