Wykład 8
Obiektowe struktury danych - c.d.
1. Obiektowa struktura danych - stos jako dynamiczna rekurencyjna struktura danych
2. Szablony -wiadomości wstępne
3. Szablony obiektowych struktur danych
1. Obiektowa struktura danych –
Przykład 1
poczatek
punkt ramka
n_ramka napis x,y
dlugosc wiersz
x, y
wysokosc dlugosc wiersz
*wiersz
*wiersz x,y,
dlugosc wysokosc
x, y NULL
stos
elem nast
elem
elem
elem nast
nast
nast
wezel
Obiekt typu stos zawiera wskaźnik poczatek wskazujący na dynamiczny
obiekt typu wezel zawierający dwa wskaźniki: elem wskazujący na obiekty z
danymi, pochodzące z rodziny praprzodka klasy abstrakcyjnej abstr oraz drugi
wskaźnik nast wskazujący na kolejny obiekt typu wezel. Obiekty z danymi
pochodzą z rodziny obiektów zastosowanej w przykładach 3 i 4 z wykładu 7. W
bieżącym przykładzie zastosowano jedynie inną strukturę do przechowania tych
samych obiektów - dynamiczną rekurencyjną strukturę danych typu stos.
#include "nram7_2.h"
#include "stos8_1.h"
...
void wyswietl(punkt*);
void wyswietl(abstr*);
void wyswietl(float, char * kom);
void main()
{ { n_ramka *n_r1, *n_r2;
ramka* r1,*r2;
napis* n1,*n2;
punkt* p1,*p2;
stos Stos;
abstr* p;
n_r1 = new n_ramka(36,10,19,8,"napis w ramce_1",0x3A); Stos.wstaw(n_r1);
n_r2 = new n_ramka(54,8,19,5,"napis w ramce_2",0x1B); Stos.wstaw(n_r2);
r1 = new ramka(50,12,10,8); Stos.wstaw(r1);
r2 = new ramka(60,9,10,5); Stos.wstaw(r2);
n1 = new napis(50,21,"napis1"); Stos.wstaw(n1);
n2 = new napis(63,18,"napis2"); Stos.wstaw(n2);
p1 = new punkt(12,13); Stos.wstaw(p1);
p2 = new punkt(12,8); Stos.wstaw(p2);
/*sposób odwołania do metod wirtualnych p_dlugosc klas ramka i punkt w celu odczytu dwóch pól o tej samej nazwie dlugosc dziedziczonych przez obiekt typu n_ramka od klasy ramka i napis - przy takim odwołaniu kompilator ustala adres
metod wirtualnych podczas kompilacji; */cout << n_r1->napis::p_dlugosc()<<' ';
cout << n_r1->ramka::p_dlugosc()<<' ';
/*sposób odwołania do dwóch pól dlugosc dziedziczonych od klas ramka i punkt w celu odczytu ich wartości - możliwy przy dostępie public do składowych*/
cout<<n_r1->napis::dlugosc<<' ';
cout<<n_r1->ramka::dlugosc<<" \n";
/*1. W metodzie wirtualnej wyswietl, czystej w klasie abstr, przedefiniowanej i
dziedziczonej od klasy punkt oraz w niezależnej funkcji wyswietl dostęp do jednej z
wynika z kolejności umieszczenia klas bazowych w liście dziedziczenia w klasie
n_ramka */
/*2. Metoda stosu dla stosu o elementach typu abstr i parametrem abstr; posiadającego funkcję niezależną o nagłówku void(*)(abstr*) np. wyswietl(abstr*)
Stos.Dla _kazdego(wyswietl); //dzięki polimorfizmowi klasy abstrakcyjnej while (!Stos.pusty())
{
p=Stos.usun();
--(*p);--(*p); //dzięki polimorfizmowi klasy abstrakcyjnej p->wyswietl(„\nmetoda”); //dzięki polimorfizmowi klasy abstrakcyjnej wyswietl((punkt*)p); //dzięki polimorfizmowi klasy wirtualnej punkt wyswietl(((punkt*)p)->odleglosc(*n_r1),"");
}
Stos.wstaw(n_r1); Stos.wstaw(n_r2);
Stos.wstaw(r1); Stos.wstaw(r2);
Stos.wstaw(n1); Stos.wstaw(n2);
Stos.wstaw(p1); Stos.wstaw(p2);
Stos.Dla_kazdego(wyswietl);
/* dzięki polimorfizmowi klasy abstrakcyjnej wywołanie destruktora stosu - usuwanie elementów stosu, pochodnych klasy abstr, dzięki jej wirtualnemu destruktorowi, przedefiniowanemu w klasach pochodnych*/
} }...
/* uniwersalna funkcja do wyświetlania pól całej rodziny obiektów: punkt, ramka, napis oraz n_ramka dzięki:
przekazywaniu obiektów przez wskaźnik
zastosowaniu polimorfizmu, czyli wywoływaniu metod wirtualnych: p_dlugosc, p_wiersz, p_wysokosc, p_atrybuty */void wyswietl(punkt* p)
{cout << "Atrybuty: " << p->p_atrybuty();
cout << " Wiersz: " << p->p_wiersz();
cout << " Dlugosc, Wysokosc: "<< p->p_dlugosc()<< " "<<p->p_wysokosc()<<" ";
cout << " Wspolrzedne: " << p->odcieta() << " "<< p->rzedna();
p->rysuj();.... }
void wyswietl(abstr* p) //funkcja do wyświetlania stosu elementów typu abstr*
{ --(*p);
p->wyswietl("");}
#include "abstr7_2.h" //zawartość pliku nagłówkowego stos8_1.h
#include <alloc.h>
class stos { struct wezel { abstr *elem;
wezel* nast;
wezel (abstr *x, wezel*y) {elem= x; nast= y;}
};
typedef wezel* polaczenie;
polaczenie poczatek;
public:
stos(int = NULL) {poczatek = NULL;}
~stos();
int pusty() {return poczatek==NULL;}
void wstaw(abstr *x)
{if (coreleft() >= sizeof(wezel))
poczatek = new wezel (x, poczatek);}
abstr* usun()
{if (!poczatek) return NULL;
abstr* v = poczatek->elem; polaczenie pom = poczatek->nast;
delete poczatek; poczatek=pom; return v;}
//uniwersalna metoda wykonująca dowolną funkcję o nagłówku void(*)(abstr*) na //elementach stosu
void Dla_kazdego(void (*p) (abstr*));
};
stos::~stos()
{ polaczenie pom= poczatek;
while (poczatek)
{ pom= poczatek->nast;
delete poczatek->elem;
delete poczatek;
poczatek= pom;}}
void stos::Dla_kazdego(void (*p)(abstr*)) { polaczenie pom= poczatek;
while(pom)
{ p(pom->elem);
pom= pom->nast;}}
2. Szablony - wiadomości wstępne
Szablon lub inaczej wzorzec (template) definiuje rodzinę typów lub funkcji.
deklaracja-wzorca:
template <lista-argumentów-wzorca> deklaracja lista-argumentów-wzorca:
argument-wzorca
lista-argumentow-wzorca, argument-wzorca argument-wzorca:
argument-typu
deklaracja-argumentu argument-typu:
class nazwa-typu
Deklaracja w deklaracji-wzorca musi deklarować lub definiować funkcję lub klasę.
Uwagi:
Argument-typu definiuje nazwę typu obowiązującą w zasięgu deklaracji wzorca.
Deklaracja-wzorca jest globalna i podlega zwykłym regułom zasięgu i kontroli dostępu, nazwa-typu obowiązuje w zasięgu deklaracji-wzorca.
W programach wieloplikowych deklaracje szablonów powinny być zawarte w
plikach nagłówkowych ze względu na ich wieloużywalność! Nie można umieścić
definicji szablonu w pliku modułowym!
2.1. Szablony klas
Deklarowanie i definiowanie wzorca klasy Przykład - Deklaracja wzorca klasy wektor o parametrze T.
template <class T> class wektor { T * p; int ile;
public: wektor (int)
T& element(int i) {return p[i]};
T& operator[] (int); //... };
Klasę można zadeklarować następująco:
nazwa-klasy-wzorca:
nazwa-wzorca <lista-arg-wzorca>
lista-arg-wzorca:
arg-wzorca
lista-arg-wzorca, arg-wzorca arg-wzorca:
wyrażenie nazwa-typu
Nazwa-klasy-wzorca (np. wektor) musi być unikatowa w programie. Typy argumentów-wzorca wyspecyfikowane w nazwie-klasy-wzorca muszą pasować do typów podanych w liście-argumentów-wzorca. Argumentami-wzorca mogą być:
typy, wyrażenia-stałe, adresy obiektów lub funkcji łączonych zewnętrznie lub statyczne składowe klasy.
Korzystanie z wzorca klasy 1) generowanie klas wzorca na podstawie danego szablonu
wektor<int> w1(20)
wektor<complex> w2(30)
typedef wektor<complex> cwekt; //cwekt synonimem wektor<complex>
cwekt w3(40);
w2[1] = w3.element(4) = complex(7, 8);
2) używanie nazwy-klasy-wzorca wszędzie tam, gdzie można używać nazwy klasy : class wektor<Punkt*>;
wektor<Ramka>* biezaca_ramka;
class wektor_fig : public wektor<Punkt*> {...};
Definiowanie metod klasy wzorca
Metoda klasy wzorca jest niejawnie funkcją wzorca, której argumentami są argumenty wzorca jej klasy:
template<class T> T& wektor<T>::operator[](int i) {...}
2.2. Szablony funkcji
Wzorzec funkcji specyfikuje, jak można konstruować poszczególne funkcje.
Specyfikuje on nieograniczony zbiór (przeciążonych) funkcji.
Każdy argument-wzorca podany w liście-argumentów wzorca musi być użyty jako argument we wzorcu funkcji.
Generowanie funkcji wzorca Np. rodzinę funkcji sortujących można zadeklarować:
template<class T> void sortuj(wektor<T>& w)
{tutaj można używać metod obiektu w klasy wektor<T> oraz typu T}
wektor<complex> wc(100);
wektor<int> wi(200);
void f (wektor<complex>& wc, wektor<int>& wi) { sortuj(wc); sortuj(wi); }
Dopasowanie argumentów template<class T> T max (T a, T b)
{return a>b ? a : b;};
void f (int a, int b, char c, char d) {
int m1 = max (a, b);
int m2 = max (c, d);
int m3 = max (a, c); //błąd: nie można wygenerować max(int, char) }
Błędne deklaracje i definicje wzorca funkcji:
template<class T> T* utworz();
template<class T> void f() { T a... }
Zasady rozstrzygania dla funkcji wzorca i innych funkcji o tej samej nazwie:
dokładne dopasowanie funkcji
lub dokładne dopasowanie wzorca, z którego można wygenerować funkcję
lub zwykły algorytm rozstrzygania przeciążenia funkcji
Przykład - wzorzec funkcji sortującej bąbelkowo
const n= 5;template <class T> void sortuj (T *w, const int ile);
void main() {
int t1[n] ={2,1,4,3,5};
float t2[n] = {3.2, 1.2, 5.3, 6.4, 4.5};
sortuj(t1, n); //wywołanie kodu funkcji sortuj dla tablicy elementów typu int sortuj(t2, n); //wywołanie kodu funkcji sortuj dla tablicy elementów typu float }
// wzorzec funkcji, który stanie się kodem konkretnej funkcji, jeżeli kompilator // zauważy wywołanie wzorca z konkretnymi danymi zamiast parametru T
template <class T> void sortuj (T *w, const int ile){
for (int i= 0; i < ile-1; i++) for (int j=0; j <ile-1-i; j++)
if (w[j] > w[j+1]) { T pom = w[j];
w[j] = w[j+1];
w[j+1]= pom;}
};
3. Szablony obiektowych struktur danych
Szablony uniwersalnych obiektowych struktur danych umożliwiają powielanie kodu dla różnych typów elementów przechowywanych w tych strukturach:
pochodzących z różnych „rodzin” obiektowych lub typów podstawowych (int, float itp.).
Przykład 2 - szablon stosu jako tablicy dynamicznych elementów - parametrów szablonu (dotyczy przykładu 4 z wykładu 7)
#include "nram7_2.h"
#include "tstos7_3.h"
...
void wyswietl(punkt*);
void wyswietl(abstr*);
void wyswietl(int*);
void wyswietl(float, char * );
void main()
{ { //wygenerowana klasa stos o elementach typu wskaźniki na int i obiekt ST stos <int> ST(4);
int *l;
l = new int; *l = 1; ST.wstaw(l);
l = new int; *l = 2; ST.wstaw(l);
l = new int; *l = 3; ST.wstaw(l);
l = new int; *l = 4; ST.wstaw(l);
/*metoda stosu o elementach typu int (parametr int); posiadającego dowolną funkcję niezależną o nagłówku void(*)(int*) np. wyswietl(int*)*/
ST.Dla_kazdego(wyswietl);
n_ramka *n_r1, *n_r2;
ramka* r1,*r2;
napis* n1,*n2;
punkt* p1,*p2;
/*wygenerowana klasa stos o elementach typu wskaźniki na obiekty z rodziny klasy abstr i obiekt Stos */
stos <abstr> Stos(8);
abstr* p;
n_r1 = new n_ramka(36,10,19,8,"napis w ramce_1",0x3A); Stos.wstaw(n_r1);
n_r2 = new n_ramka(54,8,19,5,"napis w ramce_2",0x1B); Stos.wstaw(n_r2);
r1 = new ramka(50,12,10,8); Stos.wstaw(r1);
r2 = new ramka(60,9,10,5); Stos.wstaw(r2);
n1 = new napis(50,21,"napis1"); Stos.wstaw(n1);
n2 = new napis(63,18,"napis2"); Stos.wstaw(n2);
/*metoda stosu o elementach typu abstr (parametr abstr); posiadającego funkcję niezależną o nagłówku void(*)(abstr*) np. wyswietl(abstr*)
Stos.Dla_kazdego(wyswietl);
while (!Stos.pusty()) {
p=Stos.usun();
--(*p); //dzięki polimorfizmowi klasy abstrakcyjnej p->wyswietl(""); //dzięki polimorfizmowi klasy abstrakcyjnej wyswietl(p); //dzięki polimorfizmowi klasy abstrakcyjnej wyswietl(((punkt*)p)->odleglosc(*n_r1),"");
}
/*wygenerowana klasa stos o elementach typu wskaźniki na obiekty z rodziny klasy punkt i obiekt Stos_ */
stos <punkt> Stos_(8);
punkt* p_;
Stos_.wstaw(n_r1);
Stos_.wstaw(n_r2);
Stos_.wstaw(r1);
Stos_.wstaw(r2);
Stos_.wstaw(n1);
Stos_.wstaw(n2);
Stos_.wstaw(p1);
Stos_.wstaw(p2);
/*metoda stosu o elementach typu punkt (parametr punkt); posiadającego funkcję niezależną o nagłówku void(*)(punkt*) np. wyswietl(punkt*)*/
Stos_.Dla_kazdego(wyswietl);
while (!Stos_.pusty()) {
p_= Stos_.usun();
--(*p_); //dzięki polimorfizmowi klasy wirtualnej punkt p_->wyswietl(""); //dzięki polimorfizmowi klasy wirtualnej punkt wyswietl(p_); //dzięki polimorfizmowi klasy wirtualnej punkt wyswietl(p_->odleglosc(*n_r1),"");
//wywołanie wirtualnych destruktorów z klasy aktualnych obiektów z rodziny punkt delete p_;
}
/*Dzięki destruktorowi klasy stos został usunięty stos ST elementów typu int.
Destruktory obiektów Stos i Stos_ usunęły jedynie dynamiczne tablice wskaźników w obu stosach, ponieważ obiekty z rodziny punkt zostały odłączone od nich (pętle
while) oraz usunięte z pamięci (druga pętla while) */} }
/* uniwersalna funkcja do wyświetlania pól całej rodziny obiektów: punkt, ramka, napis
przekazywaniu obiektów przez wskaźnik
zastosowaniu polimorfizmu, czyli wywoływaniu metod wirtualnych: p_dlugosc, p_wiersz, p_wysokosc, p_atrybuty*/
void wyswietl(punkt* p) {
cout << "Atrybuty: " << p->p_atrybuty();
cout << " Wiersz: " << p->p_wiersz();
cout << " Dlugosc, Wysokosc: "<< p->p_dlugosc()<< " "<<p->p_wysokosc()<<" ";
cout << " Wspolrzedne: " << p->odcieta() << " "<< p->rzedna();
p->rysuj();....
}
//funkcja do wyświetlania stosu elementów typu abstr*
void wyswietl(abstr* p) { --(*p);
p->wyswietl("");
}
//funkcja do wyświetlania stosu elementów typu int*
void wyswietl(int* w) {
cout << *w << "\n";
getch();
}
#ifndef STOS.H //zawartość pliku nagłówkowego tstos7_3.h
#define STOS.H
#include "abstr7_2.h"
const maxN=8;
template <class T>
class stos {
public:
typedef T* TT;
TT *elem; //elem jako wskaźnik dla dynamicznej tablicy wskaźników TT int ile; //liczba elementów wstawionych do tablicy
int rozmiar; //maksymalny rozmiar tablicy dynamicznej wskaźników TT stos(int max)
{ (max<=0) ? (rozmiar = maxN) : (rozmiar = max);
elem = new TT [rozmiar]; ile = 0;}
~stos();
int pusty() {return ile == 0;}
void wstaw(TT x)
{if ( ile < rozmiar) elem[ile++] = x;}
TT usun()
{ if (ile) return elem[--ile];
else return NULL;}
//uniwersalna metoda wykonująca dowolną funkcję p o nagłówku void(*)(T*) na //elementach stosu
void Dla_kazdego(void (*p) (T*)) };
template <class T>
void stos<T>::Dla_kazdego(void (*p) (T*)) { int i=ile;
while(i>0) p(elem[--i]);}
template <class T>
stos<T>::~stos() {int i=ile;
while(i>0) delete elem[--i];
delete elem;}
#endif
Przykład 3 - szablon stosu jako struktury dynamicznej typu stos przechowującej dynamiczne elementy, występujące jako parametr szablonu ( dotyczy przykładu 1)
Program ten ma postać podobną do programu z szablonem tablicy z przykładu 2.
#include "nram7_2.h"
#include "tstos8_1.h"
...
void wyswietl(punkt*); //funkcje jak w przykładzie 2 void wyswietl(abstr*);
void wyswietl(int*);
void wyswietl(float, char * kom);
void main()
{ { stos<int> ST;
int * l;
l = new int; *l = 1; ST.wstaw(l);
l = new int; *l = 2; ST.wstaw(l);
l = new int; *l = 3; ST.wstaw(l);
l = new int; *l = 4; ST.wstaw(l);
ST.Dla_kazdego(wyswietl); //uwaga z przykładu 2 n_ramka *n_r1, *n_r2;
ramka* r1,*r2;
napis* n1,*n2;
punkt* p1,*p2;
stos <abstr> Stos;
abstr* p;
n_r1 = new n_ramka(36,10,19,8,"napis w ramce_1",0x3A); Stos.wstaw(n_r1);
n_r2 = new n_ramka(54,8,19,5,"napis w ramce_2",0x1B); Stos.wstaw(n_r2);
r1 = new ramka(50,12,10,8); Stos.wstaw(r1);
r2 = new ramka(60,9,10,5); Stos.wstaw(r2);
n1 = new napis(50,21,"napis1"); Stos.wstaw(n1);
n2 = new napis(63,18,"napis2"); Stos.wstaw(n2);
p1 = new punkt(12,13); Stos.wstaw(p1);
p2 = new punkt(12,8); Stos.wstaw(p2);
Stos.Dla_kazdego(wyswietl);
while (!Stos.pusty()) { p= Stos.usun();
--(*p); //dzięki polimorfizmowi klasy abstrakcyjnej p->wyswietl(""); //dzięki polimorfizmowi klasy abstrakcyjnej wyswietl(p); //dzięki polimorfizmowi klasy abstrakcyjnej
stos <punkt> Stos_;
punkt* p_;
Stos_.wstaw(n_r1);
Stos_.wstaw(n_r2);
Stos_.wstaw(r1);
Stos_.wstaw(r2);
Stos_.wstaw(n1);
Stos_.wstaw(n2);
Stos_.wstaw(p1);
Stos_.wstaw(p2);
Stos_.Dla_kazdego(wyswietl);
while (!Stos_.pusty()) { p=Stos_.usun();
--(*p_); //dzięki polimorfizmowi klasy wirtualnej punkt p_->wyswietl(""); //dzięki polimorfizmowi klasy wirtualnej punkt wyswietl(p_); //dzięki polimorfizmowi klasy wirtualnej punkt wyswietl(p_)>odleglosc(*n_r1),"");
delete p_;
}
}}...
...
#ifndef STOS.H //zawartość pliku nagłówkowego tstos8_1.h
#define _STOS.H
#include "abstr7_2.h"
#include <alloc.h>
template <class T> class stos { struct wezel
{ T *elem;
wezel* nast;
wezel (T * x, wezel* y) {elem = x; nast = y;} };
typedef wezel* polaczenie;
polaczenie poczatek;
public:
stos(int = NULL) {poczatek = NULL;}
~stos();
int pusty() {return poczatek == NULL;}
T* usun()
{if (!poczatek) return NULL;
T* v = poczatek->elem;
polaczenie pom = poczatek->nast;
delete poczatek;
poczatek = pom; return v;}
void wstaw(T *x)
{ if (coreleft() >= sizeof(wezel)) poczatek = new wezel (x, poczatek);}
//uniwersalna metoda wykonująca dowolną funkcję p o nagłówku void(*)(T*) na //elementach stosu
void Dla_kazdego(void (*p)(T*));
};
template <class T>
void stos<T>::Dla_kazdego(void (*p)(T*)) { polaczenie pom = poczatek;
while(pom)
{ p(pom->elem); pom= pom->nast; } } template <class T>
stos<T>::~stos()
{ polaczenie pom = poczatek;
while (poczatek)
{ pom = poczatek->nast;
delete poczatek->elem; delete poczatek;
poczatek = pom;} }