Lista dwukierunkowa -- przyklad zastosowania destruktora

Transkrypt

Lista dwukierunkowa -- przyklad zastosowania destruktora
Lista dwukierunkowa – przykład zastosowania
destruktora wirtualnego
Bogdan Kreczmer
ZPCiR IIAiR PWr
pokój 307 budynek C3
[email protected]
c 2006–2008 Bogdan Kreczmer?
Copyright ? Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepiony
˛
pod
warunkiem wykorzystania wyłacznie
˛
do własnych prywatnych potrzeb i może on być kopiowany wyłacznie
˛
w całości, razem z
niniejsza˛ strona˛ tytułowa.
˛
1
Lista z zarzadc
˛ a˛ kolejki
class RozElementListy { //
public :
RozZarzadcaListy ∗ Zarzadca;
RozElementListy ∗ Poprz;
RozElementListy ∗ Naste;
RozElementListy( ) { Poprz = Naste = NULL;
RozElementListy( );
Zarzadca = NULL; }
∼
void WstawPrzed(RozElementListy ∗ElemNast);
}; //
class RozZarzadcaListy { //
public:
RozElementListy ∗ Glowa;
RozZarzadcaListy( ) { Glowa = NULL; }
∼
RozZarzadcaListy( );
void DodajNaPoczatek( RozElementListy ∗wElem);
}; //
W przykładzie przedstawionym dalej zostanie wykorzystana definicja listy dwukierunkowej przedstawiona powyżej.
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego
2
Przykład listy
struct NumerElementu { int
Numer; };
struct ElementZbioru: public RozElementListy, public NumerElementu {
ElementZbioru(int Num = 0) { Numer = Num; }
};
int main( )
{
RozZarzadcaListy ZLst;
NumerElementu ∗wNumEl = new ElementZbioru(1);
ZLst.DodajNaPoczatek(static cast<ElementZbioru∗>(wNumEl));
ZLst.DodajNaPoczatek( new ElementZbioru(2) );
for (RozElementListy ∗wELst = ZLst. Glowa; wELst; wELst = wELst–> Naste)
cout << ” Element Zbioru: ” << static cast<ElementZbioru∗>(wELst)–> Numer << endl;
cout << ”———————–” << endl;
delete wNumEl;
for (RozElementListy ∗wELst = ZLst. Glowa; wELst; wELst = wELst-> Naste)
cout << ” Element Zbioru: ” << static cast<ElementZbioru∗>(wELst)–> Numer << endl;
}
Ze wzgledu
˛
na brak destruktorów wirtualnych wynik działania programu nie jest jednoznacznie
określony. Na pewno konstrukcja taka jest błedna.
˛
Element Zbioru:
Element Zbioru:
--------------Element Zbioru:
Element Zbioru:
2
1
2
0
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego
3
Lista z wirtualnym destruktorem
class WirElementListy { //
public:
...
virtual ∼ WirElementListy( );
...
}; //
class WirZarzadcaListy { //
public:
...
virtual ∼ WirZarzadcaListy( );
...
}; //
Jeżeli przewidywane jest użycie danej klasy jako klasy bazowej oraz konieczna bedzie
˛
destrukcja
całego obiektu klasy pochodnej z wykorzystaniem wskaźnika do podobiektu wspomnianej klasy bazowej, to destruktory tej klas powinny być destruktorami wirtualnymi.
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego
4
Przykład listy
struct NumerElementu { int
Numer; virtual ∼NumerElementu( ){ } };
struct ElementZbioru: public WirElementListy, public NumerElementu {
ElementZbioru(int Num = 0) { Numer = Num; }
};
int main( )
{
WirZarzadcaListy ZLst;
NumerElementu ∗wNumEl = new ElementZbioru(1);
ZLst.DodajNaPoczatek(static cast<ElementZbioru∗>(wNumEl));
ZLst.DodajNaPoczatek( new ElementZbioru(2) );
for (RozElementListy ∗wELst = ZLst. Glowa; wELst; wELst = wELst–> Naste)
cout << ” Element Zbioru: ” << static cast<ElementZbioru∗>(wELst)–> Numer << endl;
cout << ”———————–” << endl;
delete wNumEl;
for (RozElementListy ∗wELst = ZLst. Glowa; wELst; wELst = wELst-> Naste)
cout << ” Element Zbioru: ” << static cast<ElementZbioru∗>(wELst)–> Numer << endl;
}
Wprowadzenie destruktorów wirtualnych w klasach bazowych zapewnia, iż operacje usuwania obiektów na
dowolnym poziomie bedzie
˛
poprawne.
Element Zbioru:
Element Zbioru:
--------------Element Zbioru:
2
1
2
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego
5
Modelowanie wieloboku (cz. 1)
class Wektor { //
float x, y;
public :
Wektor(float x = 0, float y = 0): x(x), y(y) { }
const float &x() const { return x; }
...
}; //
class Wierzcholek: public Wektor, public WirElementListy { //
public :
Wierzcholek(float x, float y): Wektor(x,y) { }
}; //
class Wielobok: public WirZarzadcaListy { //
public :
void DodajWierzcholek( float x, float y );
{ DodajNaPoczatek(new Wierzcholek(x,y)); }
}; //
Wykorzystujac
˛ wcześniej zdefiniowane klasy poprzez dziedziczenie możemy w miare˛ prosty sposób
budować struktury bardziej złożone. Wada˛ przedstawionej konstrukcji jest to, że odsyłacze w liście
wskazuja˛ na obiekty składowe rzeczywistych elementów kolejki
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego
6
Modelowanie wieloboku (cz. 2)
int main( )
{
Wielobok Wk;
Wk.DodajWierzcholek(1,1);
Wk.DodajWierzcholek(2,2);
Wk.DodajWierzcholek(3,3);
Wierzcholek ∗wWierz;
for ( WirElementListy ∗wElem = Wk. Glowa; wElem; wElem = wElem–> Naste ) {
wWierz = static cast<Wierzcholek∗>(wElem);
cout << ” Wierzcholek ”
<< ”(” << wWierz–>x( ) << ”, ” << wWierz–>y( ) << ”)” << endl;
}
}
Jedynym sposobem dostania sie˛ do obiektów klasy Wierzcholek jest rzutowanie wskaźników.
Lista dwukierunkowa – przykład zastosowania destruktora wirtualnego