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