Upload
dinhkiet
View
217
Download
2
Embed Size (px)
Citation preview
Polimorfizm
Część piąta
Niniejsze opracowanie zawiera skrót treści wykładu, lektura tych materiałów nie zastąpi uważnego w nim uczestnictwa.Opracowanie to jest chronione prawem autorskim. Wykorzystywanie jakiegokolwiek fragmentu w celach innych niż nauka własna jest nielegalne.
Dystrybuowanie tego opracowania lub jakiejkolwiek jego części oraz wykorzystywanie zarobkowe bez zgody autora jest zabronione.
Roman Simiński
[email protected]/~siminski
Autor
Kontakt
Wprowadzenie do programowania Wprowadzenie do programowania w języku C++w języku C++
Koncepcja polimorfizmuKoncepcja polimorfizmuPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 2Strona :
PolimorfizmJęzyk C++Język C++
poly czyli wiele,
morphos czyli postać.
Słowo polimorfizm pochodzi od dwóch greckich słów:
Polimorfizm oznacza zatem wielopostaciowość. Polimorfizm w programowaniu obiektowym oznacza zdolność obiektów do różnych zachowań, w zależności od bieżącego kontekstu wykonania programu.
Polega to zwykle na tym, że obiekt―zleceniodawca usługi przesyła komunikat do obiektu-wykonawcy usługi, który w odpowiedzi wykonuje odpowiednią w danym kontekście akcję. To jaka ma być wykonana akcja, ustalane jest na etapie wykonania programu, a wykonywane akcje mogą być różne.
W języku C++ wykonanie akcji polega na wywołaniu odpowiedniej funkcji. Polimorfizm w tym języku polega na tym, że wywoływane mogą być różne wersje tej samej funkcji.
Różne wersje tej samej funkcji powstają w trakcie dziedziczenia, kiedy to klasy pochodne w specjalny sposób redefiniują pewną funkcję składową.
Dziedziczenie „produkuje” wiele spowinowaconych klas Dziedziczenie „produkuje” wiele spowinowaconych klas Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 3Strona :
Wiele klas tworzących hierarchię rodzinną
PolimorfizmJęzyk C++Język C++
Zdyscyplinowane stosowanie zasad abstrakcji, hermetyzacji i dziedziczenia prowadzi do powstania, często rozbudowanych, hierarchii klas.
Obiekty takich klas są często do siebie podobne, ale nie jednakowe.
Problem: jak spójnie i wygodnie zarządzać grupami obiektów różnych klas należących do tej samej hierarchii, zdefiniowanej dziedziczeniem?
Auto
markamodelrokProdnrRej
. . .
AutoWSerwisie
przebiegnumerKomputerowy
. . .
AutoWStacjiDiagn
dataPrzegladunastepnyPrzegladdiagnosta
. . .
AutoWKomisie
cenadataPrzyjeciawlasciciel
. . .
ScrPixel
minx, minymaxx, maxy
. . .
Pixel
color
. . .
Point
xy
. . .
Point3D
z
. . .
Obiekty spowinowacone mogą być traktowane w specjalny sposóbObiekty spowinowacone mogą być traktowane w specjalny sposóbPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 4Strona :
PolimorfizmJęzyk C++Język C++
Auto
markamodelrokProdnrRej
. . .
AutoWSerwisie
przebiegnumerKomputerowy
. . .
AutoWStacjiDiagn
dataPrzegladunastepnyPrzegladdiagnosta
. . .
AutoWKomisie
cenadataPrzyjeciawlasciciel
. . .
ScrPixel
minx, minymaxx, maxy
. . .
Pixel
color
. . .
Point
xy
. . .
Point3D
z
. . .
Auto * auto;
auto = new AutoWKomisie;auto->rokProd = 2005;. . .delete auto;
auto = new AutoWSerwisie;auto->rokProd = 1999;. . .delete auto;
void showPointInfo( Point & p ){ cout << "X = " << p.getX() << endl; cout << "Y = " << p.getY() << endl;}. . .
Pixel p1( 10, 10, Red );Point3D p2( 1, 1, 1 );
showPointInfo( p1 );showPointInfo( p2 );
Obiekty spowinowacone mogą być traktowane w specjalny sposóbObiekty spowinowacone mogą być traktowane w specjalny sposóbPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 5Strona :
PolimorfizmJęzyk C++Język C++
W języku C++ wolno do wskaźnika klasy bazowej przypisać wskazanie obiektu klasy
pochodnej. Analogicznie jest w przypadku referencji.
Obiekt klasy pochodnej jest przecież pewnego rodzaju obiektem klasy bazowej (jest
specjalizacją klasy bazowej), posiada zatem wszystkie pola i funkcje składowe.
Wolno zatem pośrednio odwoływać się do odziedziczonych pól i funkcji składowych.
AutoWKomisie
cenadataPrzyjeciawlasciciel
. . .
Auto
markamodelrokProdnrRej
. . .
Auto * auto;
auto = new AutoWKomisie;auto->rokProd = 2005;. . .delete auto;
Zarządzanie grupami spowinowaconych obiektówZarządzanie grupami spowinowaconych obiektówPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 6Strona :
Problem
PolimorfizmJęzyk C++Język C++
Należy napisać program wspomagający pracę technologa w firmie produkującej okna. Zadaniem programu jest:
obliczanie łącznego pola powierzchni wszystkich skrzydeł okna,
przybliżonej, łącznej długości profili, użytych do produkcji każdego ze skrzydeł.
Obiekty rzeczywiste
Abstrakcyjny model analityczny
Analiza obiektowaAnaliza obiektowaPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 7Strona :
PolimorfizmJęzyk C++Język C++
Stosując zasadę abstrakcji wyodrębniamy najistotniejsze cechy obiektów dla rozpatrywanego zagadnienia — szyby to figury geometryczne a okno to ich złożenie.
Łączna powierzchnia okna to, w przybliżeniu, suma pól figur opisujących szyby,
Łączna długość profili to, w przybliżeniu, suma obwodów figur opisujących szyby.
Kwadrat
Prostokąt
Trójkąt
Prostokąt
Analiza obiektowa, cd...Analiza obiektowa, cd...Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 8Strona :
PolimorfizmJęzyk C++Język C++
Realizacja programu sprowadza się do obliczeń pól powierzchni i obwodów obiektów, będących złożeniem elementarnych figur płaskich.
Utworzyliśmy już klasy reprezentujące figury płaskie,
Nie wiemy jak reprezentować ich złożenia.
Kwadrat
Prostokąt
Trójkąt
Prostokąt
KwadratProstokąt
Trójkąt
Prostokąt
Figury płaskie raz jeszcze — budujemy hierarchię klasFigury płaskie raz jeszcze — budujemy hierarchię klasPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 9Strona :
PolimorfizmJęzyk C++Język C++
class Figure{ public :
Figure() {} double area() const { return 0; } double circumference() const { return 0; }
};
Rozpoczynamy od utworzenia nieco dziwnej klasy — reprezentującej abstrakcyjną figurę geometryczną. Wyposażamy ją w funkcje obliczania pola i obwodu.
Klasa Figure służyć będzie jak klasa bazowa dla specjalizowanych klas pochodnych, reprezentujących konkretne figury geometryczne.
Jej istotą jest stwierdzenie, że każda figura geometryczna powinna umieć wyznaczać swoje pole i obwód, w charakterystyczny dla siebie sposób.
Zatem każda z klas pochodnych, powinna redefiniować funkcje area i circumference, w odpowiedni dla tych klas sposób.
Figury płaskie raz jeszcze — budujemy hierarchię klas, klasa SquareFigury płaskie raz jeszcze — budujemy hierarchię klas, klasa SquarePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 10Strona :
PolimorfizmJęzyk C++Język C++
class Square: public Figure{ public :
Square(); Square( double startSide );
void setSide( double newSide ); double getSide();
double area() const; double circumference() const;
private: double side;};
Klasa reprezentująca kwadrat będzie teraz klasą pochodną w stosunku do klasy Figure:
double Square::area() const { return side * side; }
double Square::circumference() const { return 4 * side; }
Redefinicja funkcji area i circumference — obliczenia dla kwadratu:
Figury płaskie raz jeszcze — budujemy hierarchię klas, klasa SquareFigury płaskie raz jeszcze — budujemy hierarchię klas, klasa SquarePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 11Strona :
PolimorfizmJęzyk C++Język C++
Klasa reprezentująca prostokąt będzie teraz klasą pochodną w stosunku do klasy Figure:
class Rectangle: public Figure{ public :
Rectangle(); Rectangle( double startWidth, double startHeight );
void setWidth( double newWidth ); void setHeight( double newHeight );
double getWidth() const; double getHeight() const;
double area() const; double circumference() const;
private: double width, height;};
double Rectangle::area() const { return width * height; }double Rectangle::circumference() const { return 2 * width + 2 * height; }
Redefinicja funkcji area i circumference — obliczenia dla prostokąta:
Figury płaskie raz jeszcze — budujemy hierarchię klas, klasa TriangleFigury płaskie raz jeszcze — budujemy hierarchię klas, klasa TrianglePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 12Strona :
PolimorfizmJęzyk C++Język C++
Klasa reprezentująca prostokąt będzie teraz klasą pochodną w stosunku do klasy Figure:
class Triangle: public Figure{ public : Triangle(); Triangle( double startBase, double startHeight ); void setBase( double newBase ); void setHeight( double newHeight ); double getBase() const; double getHeight() const;
double area() const; double circumference() const;
private: double base, height;};
double Triangle::area() const { return 0.5 * base * height; }double Triangle::circumference() const { return sqrt( base * base + height * height ) + base + height; }
Redefinicja funkcji area i circumference — obliczenia dla trójkąta:
Uwaga, zakładamy dla uproszczenia, że
trójkątne szyby będą trójkątami prostokątnymi.
Hierarchia klas figur płaskichHierarchia klas figur płaskichPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 13Strona :
PolimorfizmJęzyk C++Język C++
Figure
+double area()+double circumference()
Triangle
+double area()+double circumference()
―double base―double width
Square
+double area()+double circumference()
―double side
Rectangle
+double area()+double circumference()
―double height―double width
Wykorzystanie wskaźników w hierarchii klasWykorzystanie wskaźników w hierarchii klasPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 14Strona :
PolimorfizmJęzyk C++Język C++
Figure * fp;
Załóżmy, że zdefiniowano wskaźnik do klasy bazowej Figure:
Square s( 10 );Rectangle r( 10, 20 );Triangle t( 10,10 );
Załóżmy, że zdefiniowano obiekty klas pochodnych:
fp = &s; // OK
Wiemy już, że można przypisać do wskaźnika fp wskazanie na obiekt klasy pochodnej:
fp = &r; // OK
lub
fp = &t; // OK
lub
Wykorzystanie wskaźników w hierarchii klas, cd...Wykorzystanie wskaźników w hierarchii klas, cd...Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 15Strona :
PolimorfizmJęzyk C++Język C++
W języku C++ wolno do wskaźnika klasy bazowej przypisać wskazanie obiektu klasy pochodnej. Wolno zatem pośrednio odwoływać się do odziedziczonych pól i funkcji
składowych.
fp = &s;cout << "Pole kwadratu wynosi: " << fp->area() << endl;cout << "Obwod kwadratu wynosi: " << fp->circumference() << endl;
Można zatem:
fp->setSide( 100 ); // Niedozwolone
Ale nie można:
( ( Square * )fp )->setSide( 100 );
Choć można dokonać „wymuszenia” stosując brutalne rzutowanie typów:
W C++ istnieją inne, bezpieczniejsze metody konwersji typów (np. static_cast, dynamic_cast).
Wykorzystanie wskaźników w hierarchii klas, cd...Wykorzystanie wskaźników w hierarchii klas, cd...Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 16Strona :
PolimorfizmJęzyk C++Język C++
Wykorzystując wskaźnik do klasy bazowej można odwoływać się w legalny sposób do wszystkich odziedziczonych składowych obiektu pochodnego.
Stosując zwykłe rzutowanie (lub specjalne metody konwersji) można siłowo wymusić dostęp do składowych zdefiniowanych w klasie pochodnej.
Klasa bazowa definiuje zatem „protokół” wykorzystania obiektu klasy pochodnej.
Square
―double side
Figure
+double area()+double circumference()
fp = &s;cout << fp->area() << endl;cout << fp->circumference() << endl;
Po co ten cyrk ze wskaźnikami?Po co ten cyrk ze wskaźnikami?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 17Strona :
PolimorfizmJęzyk C++Język C++
Można napisać uniwersalną funkcję wyświetlającą informacje o figurze:
void showFigureInfo( Figure * fp ){ cout << "Pole : " << fp->area() << endl; cout << "Obwod : " << fp->circumference() << endl; cout << endl;}
I wywoływać ją z różnymi obiektami klas pochodnych:
showFigureInfo( &s );showFigureInfo( &r );showFigureInfo( &t );
Po co ten cyrk ze wskaźnikami?Po co ten cyrk ze wskaźnikami?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 18Strona :
PolimorfizmJęzyk C++Język C++
Można rozbudować klasy o dodatkową funkcję określającą typ figury:
class Figure{ public : . . . char * getName() const { return "Figura"; }
};
class Square: public Figure{ public : . . . char * getName() const { return "Kwadrat"; }
};
Po co ten cyrk ze wskaźnikami?Po co ten cyrk ze wskaźnikami?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 19Strona :
PolimorfizmJęzyk C++Język C++
Można rozbudować klasy o dodatkową funkcję określającą typ figury:
class Rectangle: public Figure{ public : . . . char * getName() const { return "Prostokat"; }
};
class Triangle: public Figure{ public : . . . char * getName() const { return "Trojkat"; }
};
Po co ten cyrk ze wskaźnikami?Po co ten cyrk ze wskaźnikami?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 20Strona :
PolimorfizmJęzyk C++Język C++
Nowa wersja funkcji wyświetlającej informacje o figurach:
void showFullFigureInfo( Figure * fp ){ cout << fp->getName() << endl; cout << "Pole : " << fp->area() << endl; cout << "Obwod : " << fp->circumference() << endl; cout << endl;}
Zobaczmy w końcu, czy to działa...Zobaczmy w końcu, czy to działa...Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 21Strona :
PolimorfizmJęzyk C++Język C++
void showFullFigureInfo( Figure * fp ){ cout << fp->getName() << endl; cout << "Pole : " << fp->area() << endl; cout << "Obwod : " << fp->circumference() << endl; cout << endl;}
Wywołujemy:
showFigureInfo( &s );showFigureInfo( &r );showFigureInfo( &t );
I otrzymujemy...
To nie działa!!!
Co się dzieje?Co się dzieje?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 22Strona :
PolimorfizmJęzyk C++Język C++
Przypisanie konkretnego ciała funkcji do wywołania nazywa się wiązaniem (ang. binding).
W języku C++ występują dwa rodzaje wiązania:
Wczesne wiązanie (ang. early binding) polega na przypisaniu konkretnej funkcji każdemu wywołaniu już na etapie kompilacji programu. Inna nazwa — wiązanie statyczne (ang. static binding).
Późne wiązanie (ang. late binding) polega na przypisaniu konkretnej funkcji każdemu wywołaniu dopiero na etapie wykonania programu, w zależności od typu obiektu, którego wywołanie dotyczy. Inna nazwa to dynamiczne wiązanie (ang. dynamic binding).
Jakie wiązanie stosuje kompilator?Jakie wiązanie stosuje kompilator?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 23Strona :
PolimorfizmJęzyk C++Język C++
Figure * fp;. . .fp = &s;cout << "Pole kwadratu wynosi: " << fp->area() << endl;cout << "Obwod kwadratu wynosi: " << fp->circumference() << endl;
Wywołanie odbywa się za pośrednictwem wskaźnika do typu Figure. Kompilator „przygląda” się definicji klasy i deklaracji wywoływanej funkcji:
class Figure{ . . . char * getName() const { return "Figura"; } double area() const { return 0; } double circumference() const { return 0; }};
Wywoływane funkcje są „zwykłymi” funkcjami.
Takie funkcje są łączone statycznie. Kompilator wstawia wywołanie funkcji zdefiniowanych w klasie występującej w deklaracji wskaźnika fp.
Wywoływane są zatem funkcje z klasy Figure, niezależnie od klasy wskazywanego obiektu.
Wiązanie statyczne (wczesne)Wiązanie statyczne (wczesne)Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 24Strona :
PolimorfizmJęzyk C++Język C++
O tym która funkcja jest wywoływana decyduje kompilator na etapie kompilacji.
Wywołanie ma taką samą postać jak wywołanie klasycznych funkcji w języku C.
O tym która funkcja zostanie wywołana decyduje typ występujący w deklaracji zmiennej wskaźnikowej.
+double area()+double circumference()
―double side
Square
Figure
+double area()+double circumference()
Figure * fp;Square s( 10 );
fp = &s;cout << fp->area() << endl;cout << fp->circumference() << endl;
Mimo iż klasa Square przedefiniowała obie funkcje i posiada ich własne wersje, nie zostaną one wywołane.
Wiązanie statyczne przyczyną problemów z showFullInfo Wiązanie statyczne przyczyną problemów z showFullInfo Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 25Strona :
PolimorfizmJęzyk C++Język C++
O tym która funkcja jest wywoływana decyduje kompilator na etapie kompilacji.
Wywołanie ma taką samą postać jak wywołanie klasycznych funkcji w języku C.
O tym która funkcja zostanie wywołana decyduje typ występujący w deklaracji zmiennej wskaźnikowej.
void showFullFigureInfo( Figure * fp ){ cout << fp->getName() << endl; cout << "Pole : " << fp->area() << endl; cout << "Obwod : " << fp->circumference() << endl; cout << endl;}
showFigureInfo( &s );showFigureInfo( &r );showFigureInfo( &t );
class Figure{ . . . char * getName() const { return "Figura"; } double area() const { return 0; } double circumference() const { return 0; }};
Mimo, że funkcja wywoływana jest dla obiektów klas potomnych względem Figure, kompilator wstawi wywołanie funkcji zgodnie z typem występującym w deklaracji
parametru fp: void showFullFigureInfo( Figure * fp )
Co trzeba zrobić, aby wszystko działało tak, jak się spodziewamy?Co trzeba zrobić, aby wszystko działało tak, jak się spodziewamy?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 26Strona :
PolimorfizmJęzyk C++Język C++
Dokonujemy drobnej modyfikacji deklaracji funkcji składowych klasy Figure:
class Figure{ public :
Figure() {} virtual double area() const { return 0; } virtual double circumference() const { return 0; } virtual char * getName() const { return "Figura"; }
};
Rekompilujemy, uruchamiamy i ...
Funkcja showFullInfo działa teraz poprawnie
Dlaczego teraz działa? Bo kompilator stosuje wiązanie dynamiczneDlaczego teraz działa? Bo kompilator stosuje wiązanie dynamicznePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 27Strona :
PolimorfizmJęzyk C++Język C++
O tym która funkcja jest wywoływana decyduje kompilator na etapie kompilacji.
Wywołanie ma taką samą postać jak wywołanie klasycznych funkcji w języku C.
O tym która funkcja zostanie wywołana decyduje typ występujący w deklaracji zmiennej wskaźnikowej.
void showFullFigureInfo( Figure * fp ){ cout << fp->getName() << endl; cout << "Pole : " << fp->area() << endl; cout << "Obwod : " << fp->circumference() << endl; cout << endl;}
showFigureInfo( &s );showFigureInfo( &r );showFigureInfo( &t );
class Figure{ . . . virtual double area() const { return 0; } virtual double circumference() const { return 0; } virtual char * getName() const { return "Figura"; }};
Mimo, iż typ występujący w deklaracji wskaźnika to Figure, wywołane zostaną funkcje
należące do klasy obiektu wskazywanego przez wskaźnik fp. Przy wiązaniu dynamicznym istotny jest typ obiektu wskazywanego!
Funkcje wirtualneFunkcje wirtualnePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 28Strona :
PolimorfizmJęzyk C++Język C++
Wiązanie dynamiczne jest realizowane w języku C++ za pomocą funkcji wirtualnych.
Funkcja wirtualna posiada w swojej deklaracji słowo kluczowe virtual.
Jeżeli w klasie pochodnej funkcja wirtualna nie zostanie przedefiniowana, wszystko działa jak w przypadku innych funkcji.
Jeżeli w klasie pochodnej funkcja wirtualna zostanie przedefiniowana, to zostanie ona wywołana zamiast funkcji przedefiniowanej — w przypadku gdy wskaźnik do klasy bazowej wskazuje na obiekt klasy pochodnej.
W przypadku funkcji wirtualnych nie jest istotny typ wskaźnika, wywoływana jest funkcja zdefiniowana w klasie obiektu wskazywanego.
Wiązanie dynamiczne (późne)Wiązanie dynamiczne (późne)Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 29Strona :
PolimorfizmJęzyk C++Język C++
O tym która funkcja jest wywoływana decyduje typ obiektu wskazywanego.
Funkcja wiązana dynamicznie musi być zadeklarowana jako wirtualna.
Wystarczy, że słowo kluczowe virtual wystąpi w deklaracji klasy bazowej.
+double area()+double circumference()
―double side
Square
Figure
+double area()+double circumference()
Figure * fp;Square s( 10 );
fp = &s;cout << fp->area() << endl;cout << fp->circumference() << endl;
Klasa Square przedefiniowała obie funkcje wirtualne. To one właśnie zostaną wywołane a nie funkcje z klasy bazowej.
Jak wykorzystać późne wiązanie?Jak wykorzystać późne wiązanie?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 30Strona :
PolimorfizmJęzyk C++Język C++
Zadany jest układ okna oraz wymiary jego ościeżnic. Jedna ościeżnica jest kwadratowa (bok 50cm), pozostałe trzy prostokątne (70x50 cm, 50x100 cm i 70x100 cm).
Należy wyznaczyć łączną powierzchnię szyb i długość profili.
50 7050
100
Jak reprezentować informacje o oknie?Jak reprezentować informacje o oknie?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 31Strona :
PolimorfizmJęzyk C++Język C++
const int itemNo = 4;
Figure * win[ itemNo ];
50 70
50100
win
50
50
70
50
50
100
70
100
Kwadrat Prostok tą Prostok tą Prostok tą
0 1 2 3
Definiowanie elementów okna, obliczenia, sprzątanieDefiniowanie elementów okna, obliczenia, sprzątaniePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 32Strona :
PolimorfizmJęzyk C++Język C++
// Okre lenie liczby elementówśconst int itemNo = 4;
// Tablica wka ników na elementy oknaźFigure * win[ itemNo ];
// Przydział pami ci dla elementów oknaęwin[ 0 ] = new Square( 50 );win[ 1 ] = new Rectangle( 50, 70 );win[ 2 ] = new Rectangle( 50, 100 );win[ 3 ] = new Rectangle( 70, 100 );
// Oblicz co trzeba i wyprowadz do str.wyj ciowegoścalcAndShowWinInfo( win, 4 );
// Zwolnij pami ć przydzielon elementom okna ę ąfor( int i = 0; i < itemNo; delete win[ i++] ) ;
Jak to wygląda w pamięci operacyjnej?Jak to wygląda w pamięci operacyjnej?Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 33Strona :
PolimorfizmJęzyk C++Język C++
win
Square
+double area()+double circumference()
―double side: 50
Rectangle
+double area()+double circumference()
―double height: 50―double width: 70
Rectangle
+double area()+double circumference()
―double height: 50―double width: 100
Rectangle
+double area()+double circumference()
―double height: 70―double width: 100
0 1 2 3
Funkcja calcAndShowWinInfo Funkcja calcAndShowWinInfo Podstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 34Strona :
PolimorfizmJęzyk C++Język C++
void calcAndShowWinInfo( Figure * window[], int numOfSash ){ // Tutaj ł czna powierzchnia szyb i długo ć profilią ś double totalArea = 0, totalCircum = 0;
// Zliczanie powierzchni i długo ciś for( int i = 0; i < numOfSash; i++ ) { totalArea += window[ i ]->area(); totalCircum += window[ i ]->circumference(); }
// Wyprowadzanie wyników obliczeń cout << "Powierzchnia szyb : " << totalArea << endl; cout << "Dlugosc profili : " << totalCircum << endl; cout << endl;}
Iteracja zliczającaIteracja zliczającaPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 35Strona :
PolimorfizmJęzyk C++Język C++
for( int i = 0; i < numOfSash; i++ ){ totalArea += window[ i ]->area(); totalCircum += window[ i ]->circumference();}
win
Square
+double area()+double circumference()
―double side: 50
Rectangle
+double area()+double circumference()
―double height: 50―double width: 70
Rectangle
+double area()+double circumference()
―double height: 50―double width: 100
Rectangle
+double area()+double circumference()
―double height: 70―double width: 100
i 0
0 1 2 3
double Square::area() const { return side * side; }double Square::circumference() const { return 4 * side; }`
Iteracja zliczającaIteracja zliczającaPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 36Strona :
PolimorfizmJęzyk C++Język C++
for( int i = 0; i < numOfSash; i++ ){ totalArea += window[ i ]->area(); totalCircum += window[ i ]->circumference();}
win
Square
+double area()+double circumference()
―double side: 50
Rectangle
+double area()+double circumference()
―double height: 50―double width: 70
Rectangle
+double area()+double circumference()
―double height: 50―double width: 100
Rectangle
+double area()+double circumference()
―double height: 70―double width: 100
i 1
0 1 2 3
double Rectangle::area() const { return width * height; }double Rectangle::circumference() const { return 2 * width + 2 * height; }
Iteracja zliczającaIteracja zliczającaPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 37Strona :
PolimorfizmJęzyk C++Język C++
for( int i = 0; i < numOfSash; i++ ){ totalArea += window[ i ]->area(); totalCircum += window[ i ]->circumference();}
win
Square
+double area()+double circumference()
―double side: 50
Rectangle
+double area()+double circumference()
―double height: 50―double width: 70
Rectangle
+double area()+double circumference()
―double height: 50―double width: 100
Rectangle
+double area()+double circumference()
―double height: 70―double width: 100
i 2
0 1 2 3
double Rectangle::area() const { return width * height; }double Rectangle::circumference() const { return 2 * width + 2 * height; }
Iteracja zliczającaIteracja zliczającaPodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 38Strona :
PolimorfizmJęzyk C++Język C++
for( int i = 0; i < numOfSash; i++ ){ totalArea += window[ i ]->area(); totalCircum += window[ i ]->circumference();}
win
Square
+double area()+double circumference()
―double side: 50
Rectangle
+double area()+double circumference()
―double height: 50―double width: 70
Rectangle
+double area()+double circumference()
―double height: 50―double width: 100
Rectangle
+double area()+double circumference()
―double height: 70―double width: 100
i 3
0 1 2 3
double Rectangle::area() const { return width * height; }double Rectangle::circumference() const { return 2 * width + 2 * height; }
Polimorfizm — krótkie podsumowaniePolimorfizm — krótkie podsumowaniePodstawy i języki programowaniaPodstawy i języki programowania
Copyright © Roman Simiński 39Strona :
PolimorfizmJęzyk C++Język C++
Polimorfizm zakłada, że jedna funkcja składowa występować może we wielu wersjach.
Wersje te definiowane są w klasach pochodnych, polimorfizm zakłada zatem wykorzystanie dziedziczenia.
Wysłanie do obiektu komunikatu o określonej nazwie (np. area) może spowodować wykonanie różnych akcji (wywołanie różnych wersji funkcji składowej) w zależności od kontekstu wywołania.
Kontekst wywołania zależy od tego, na obiekt jakiej klasy wskazuje zmienna wskaźnikowa, albo z jakim obiektem skojarzona jest referencja.
W języku C++ polimorfizm jest realizowany z wykorzystaniem funkcji wirtualnych i dostępny jest przy wykorzystaniu odwołań wskaźnikowych lub referencyjnych.
Funkcje wirtualne wywoływane są na zasadzie wiązania dynamicznego.
C++ jest hybrydowym językiem programowania. Zawiera mechanizmy obiektowe ale nie wymusza ich wykorzystania. Pozostawia również programiście wybór, czy stosować wiązanie styczne czy dynamiczne.