TEMAT : KLASY POLIMORFIZM 1. Wprowadzenie do polimorfizmu i funkcji wirtualnych w języku C++ Język C++ zakłada, że w pewnych przypadkach uzasadnione jest tworzenie klas, których reprezentanci w programie (obiekty) nie będą przedstawiały żadnego konkretnego obiektu fizycznego czy myślowego. Klasy takie nazywane są klasami abstrakcyjnymi. W odróżnieniu od klas abstrakcyjnych, pod pojęciem klas konkretnych rozumiemy takie klasy, których obiekty mogą reprezentować określony obiekt fizyczny lub myślowy. Klasy abstrakcyjne wygodnie jest wykorzystywać jako klasy podstawowe w przypadku programowania obiektowego opartego na dziedziczeniu. Zadaniem klas abstrakcyjnych jest dostarczenie pewnego interfejsu klasom pochodnym, które dziedziczą z klas abstrakcyjnych. Dana klasa staje się klasą abstrakcyjną, gdy przynajmniej jedna z jej funkcji składowych jest zadeklarowana jako tzw. czysta funkcja wirtualna (w deklaracji jest ona zainicjowana wartością zero). Co ważne, nie jest możliwe utworzenie obiektu klasy abstrakcyjnej. Czyste funkcje wirtualne powinny być zdefiniowane w klasach pochodnych. Polimorfizm (wielopostaciowość) jest mechanizmem języka C++, który umożliwia programiście na abstrakcyjne traktowanie określonych wyrażeń (niezależność od konkretnych typów). Zadanie Zaproponuj implementację w programie trzech klas: klasy abstrakcyjnej CFigura (będącej klasą podstawową), klas konkretnych CKolo i CKwadrat (będących klasami pochodnymi klasy CFigura). CFigura CKolo CKwadrat #pragma once Implementacja klasy CFigura jako abstrakcyjnej klasy podstawowej #include <iostream> #include <string> using namespace std; class CFigura public: CFigura(string nazwa_t = ""); ~CFigura(); void SetNazwa (string nazwa_t); string GetNazwa (); void Drukuj () const; virtual void Drukuj_FunkcjaWirtualna () const; virtual double Pole () const = 0; protected: string nazwa; ; CFigura::CFigura(string nazwa_t) SetNazwa(nazwa_t); CFigura::~CFigura() void CFigura::SetNazwa (string nazwa_t) nazwa = nazwa_t; 1
string CFigura::GetNazwa () return nazwa; void CFigura::Drukuj () const cout << "Nazwa = " << nazwa << endl; void CFigura::Drukuj_FunkcjaWirtualna () const cout << "Nazwa = " << nazwa << endl; Implementacja pochodnej klasy konkretnej CKolo (dziedziczącej z abstrakcyjnej klasy podstawowej CFigura) #pragma once #include "figura.h" class CKolo : public CFigura public: CKolo(double R_t = 1.0, string nazwa_t = ""); ~CKolo(); void SetDlugoscPromienia (double R_t); double GetDlugoscPromienia () const; virtual void Drukuj () const; virtual void Drukuj_FunkcjaWirtualna () const; virtual double Pole () const; private: double R; ; CKolo::CKolo(double R_t, string nazwa_t) : CFigura(nazwa_t) SetDlugoscPromienia (R_t); CKolo::~CKolo() void CKolo::SetDlugoscPromienia (double R_t) if (R_t > 0) R = R_t; else R = 1.0; double CKolo::GetDlugoscPromienia() const return R; double CKolo::Pole () const return 3.1415*R*R; void CKolo::Drukuj () const cout << "R = " << R << endl; 2
void CKolo::Drukuj_FunkcjaWirtualna () const cout << "R = " << R << endl; Implementacja pochodnej klasy konkretnej CKwadrat (dziedziczącej z abstrakcyjnej klasy podstawowej CFigura) #pragma once #include <string> #include "figura.h" using namespace std; class CKwadrat : public CFigura public: CKwadrat(double a_t = 1.0, string nazwa_t = ""); ~CKwadrat(); void SetDlugoscBoku (double a_t); double GetDlugoscBoku () const; virtual void Drukuj () const; virtual void Drukuj_FunkcjaWirtualna () const; virtual double Pole () const; private: double a; ; CKwadrat::CKwadrat(double a_t, string nazwa_t) : CFigura(nazwa_t) SetDlugoscBoku(a_t); CKwadrat::~CKwadrat() void CKwadrat::SetDlugoscBoku (double a_t) if (a_t > 0) a = a_t; else a = 1.0; double CKwadrat::GetDlugoscBoku () const return a; double CKwadrat::Pole () const return a*a; void CKwadrat::Drukuj () const cout << "a = " << a << endl; void CKwadrat::Drukuj_FunkcjaWirtualna () const cout << "a = " << a << endl; 3
Wykorzystanie klas CFigura, CKolo oraz CKwadrat w programie #include "stdafx.h" #include <iostream> #include <conio.h> using namespace std; #include "Figura.h" #include "Kolo.h" #include "Kwadrat.h" // Przyklady wiazania dynamicznego w funkcjach 'DrukujPtr' i 'DrukujRef' void FunkcjaPtr (CFigura * fig) cout << "Wypisz informacje o figurze" << endl; cout << "Nazwa = " << fig->getnazwa() << ", pole = " << fig->pole() << endl; void FunkcjaRef (CFigura & fig) cout << "Wypisz informacje o figurze" << endl; cout << "Nazwa = " << fig.getnazwa() << ", pole = " << fig.pole() << endl; int _tmain(int argc, _TCHAR* argv[]) // Deklaracja obiektu CFigura nie jest mozliwa w programie, // gdyz nie mozemy deklarowac obiektow klasy abstrakcyjnej //CFigura moja_figura; //moja_figura.setnazwa("nieznana-figura"); // (1) CKolo kolo_bez_nazwy (2.5); CKolo kolo_nieznane; cout << "Figura 1-A:" << endl; cout << " Nazwa = " << kolo_bez_nazwy.getnazwa() << ", R = " << kolo_bez_nazwy.getdlugoscpromienia() << endl; cout << "Figura 1-B:" << endl; cout << " Nazwa = " << kolo_nieznane.getnazwa() << ", R = " << kolo_nieznane.getdlugoscpromienia() << endl; // (2) CKolo kolo(2.5,"moje-kolo"); cout << "Figura 2:" << endl; cout << " Nazwa = " << kolo.getnazwa() << ", R = " << kolo.getdlugoscpromienia() << ", Pole = " << kolo.pole() << endl; // (3) CKwadrat kwadrat(2.5,"moj-kwadrat"); cout << "Figura 3:" << endl; cout << " Nazwa = " << kwadrat.getnazwa() << ", a = " << kwadrat.getdlugoscboku() << ", Pole = " << kwadrat.pole() << endl; // Tablica obiektow typu CFigura CFigura * tab[4]; tab[0] = &kolo_bez_nazwy; tab[1] = &kolo_nieznane; tab[2] = &kolo; tab[3] = &kwadrat; 4
for (int i=0; i<=3; i++) cout << "Pole figury " << i+1 << " o nazwie " << tab[i]->getnazwa() << " = " << tab[i]->pole() << endl; // Przyklady wiazania dynamicznego w funkcjach 'DrukujPtr' i 'DrukujRef' FunkcjaPtr (&kolo); FunkcjaRef (kwadrat); //double dl_boku; //cout << "Podaj dlugosc boku a = "; cin >> dl_boku; //kwadrat.setdlugoscboku(dl_boku); // Wywolanie funkcji drukujacej dane w obiekcie CKolo KoloA(5.0,"KoloA"); CFigura * pfiguraa = &KoloA; CKwadrat KwadratB(6.0,"KwadratB"); CFigura & reffigurab = KwadratB; // Drukujemy obiekty KoloA i KwadratB wykorzystujac do tego funkcje // Drukuj_Wariant1 oraz Drukuj_Wariant2 cout << "" << endl; cout << "Drukujemy obiekt KoloA " << endl; cout << "wykorzystujac funkcje 'Drukuj' oraz 'Drukuj_FunkcjaWirtualna'" << endl; cout << "" << endl; cout << "KoloA poprzez obiekt klasy CKolo:" << endl; KoloA.Drukuj(); cout << "KoloA poprzez wskaznik do klasy CFigura i f. skladowa klasy:" << endl; pfiguraa->drukuj(); cout << "KoloA poprzez wskaznik do klasy CFigura i f. wirtualna:" << endl; pfiguraa->drukuj_funkcjawirtualna(); cout << "" << endl; cout << "Drukujemy obiekt KwadratB " << endl; cout << "wykorzystujac funkcje 'Drukuj' oraz 'Drukuj_FunkcjaWirtualna'" << endl; cout << "" << endl; cout << "KwadratB poprzez obiekt klasy CKwadrat:" << endl; KwadratB.Drukuj(); cout << "KwadratB poprzez referencje do zmien. typu CKwadrat i f. kladowa klasy:" << endl; reffigurab.drukuj(); cout << "KwadratB poprzez referencje do zmien. typu CKwadrat i f. wirtualna:" << endl; reffigurab.drukuj_funkcjawirtualna(); _getch(); return 0; Wydruk wyników działania programu Figura 1-A: Nazwa =, R = 2.5 Figura 1-B: Nazwa =, R = 1 5
Figura 2: Nazwa = Moje-kolo, R = 2.5, Pole = 19.6344 Figura 3: Nazwa = Moj-kwadrat, a = 2.5, Pole = 6.25 Pole figury 1 o nazwie = 19.6344 Pole figury 2 o nazwie = 3.1415 Pole figury 3 o nazwie Moje-kolo = 19.6344 Pole figury 4 o nazwie Moj-kwadrat = 6.25 Wypisz informacje o figurze Nazwa = Moje-kolo, pole = 19.6344 Wypisz informacje o figurze Nazwa = Moj-kwadrat, pole = 6.25 Drukujemy obiekt KoloA wykorzystujac funkcje 'Drukuj' oraz 'Drukuj_FunkcjaWirtualna' KoloA poprzez obiekt klasy CKolo: Nazwa = KoloA R = 5 KoloA poprzez wskaznik do klasy CFigura i f. skladowa klasy: Nazwa = KoloA KoloA poprzez wskaznik do klasy CFigura i f. wirtualna: Nazwa = KoloA R = 5 Drukujemy obiekt KwadratB wykorzystujac funkcje 'Drukuj' oraz 'Drukuj_FunkcjaWirtualna' KwadratB poprzez obiekt klasy CKwadrat: Nazwa = KwadratB a = 6 KwadratB poprzez referencje do zmien. typu CKwadrat i f. kladowa klasy: Nazwa = KwadratB KwadratB poprzez referencje do zmien. typu CKwadrat i f. wirtualna: Nazwa = KwadratB a = 6 Analiza istotnych elementów klas CFigura, CKolo oraz CKwadrat z punktu widzenia mechanizmu polimorfizmu 6