Taki kod jest składniowo poprawny. Nie oznacza to także, że taki program skompiluje się, uruchomi, wykona i będzie wykazywać w sposób powtarzal- ny nieprawidłowe rezultaty. To znaczy tylko tyle, że rezultaty działania takiego kodu okażą się nieokreślone. W istocie rezultaty te są zależne od platformy uruchomieniowej, od tego, w jaki sposób aplikacja okaże się zależna od środowiska operacyjnego. System może się zawiesić, program może zadziałać w sposób nieprawidłowy (znienacka), może też przez pe- wien czas działać całkowicie poprawnie (do czasu). Na listingu 11.3 pokazano kompletny program zawierający implementację tej wadliwej konstrukcji. Wyjściowy wydruk tego programu na monitorze autora przedstawiono na ry- sunku 11.10. Listing 11.3. Przeciążenie operatora konkatenacji z obiektem — parametrem przekazywanym poprzez wartość ; 7 0 ! !" ; #$ & & &( ) ; 7 & & &( /; 5 ( 0 2# ; && ( !& ; 582 Część II n Programowanie obiektowe w C++ Rysunek 11.10. Wydruk wyjściowy programu z listingu 11.3 1 7 ) ! &( 7 &<& ! , ; ""; # # =2%> 1 ##?@AA 3 % =$> # $ , 54 5) ( B ( CD ; ""; 7 # 5) & () # =2%> 5 (.( ) ( 1 ##?@AA 3 % < 0 5 , &( () & 0 ; ""/; , 0 &<&6 ; "" 2# ; )E # 2 5& 5)E 7 # = 2 %> (.. )E ( 1 ##?@AA 3 % < 0 5 &( . 0)E & & ( . 0)E & F )E # , F &.E 7 ; "" 4 . , ; ""1 => ! . 0. *% 5 =*%> # $ , &4 54 &' ; G " ; G ( ; ? " ; ? F '()E <+ # & ( CD Rozdział 11. n Konstruktory i destruktory — potencjalne problemy583 # & ( CD 2# 2# # & ( CD # & !0 CD 1+A 1 ! + & 0 " ; ? ( H!H+ * & # OOOO $ , Zauważ, że te wszystkie okropne rzeczy dzieją się w chwili zakończenia działania funkcji. Pierwsze nieprzyjemne zdarzenie miało miejsce wtedy, gdy przeciążająca funkcja operatorowa zbliżała się ku końcowi i musiało (zgodnie z zasadami) nastąpić wywołanie de- struktora wobec formalnego parametru tej funkcji. Wtedy to bieżący argument tej funkcji, obiekt , został pozbawiony przydzielonej mu na stercie dynamicznej pamięci. Drugie nie- miłe zdarzenie miało miejsce, gdy funkcja-klient, zbliżała się ku końcowi i obiekt musiał zostać usunięty, ponieważ kończył się zakres przestrzeni widoczności jego nazwy (obiekt wychodził poza zakres — ang. out of scope). Jego pamięć na stercie została wtedy zwolniona i zwrócona po raz wtóry. W istocie, w C++ błędem jest powtórne zwracanie dynamicznej pamięci poprzez ponowne użycie operatora wobec tego samego niezerowego wskaźnika. Jeśli wskaźnik za- wiera ,-.., to nie jest błąd, lecz operacja pusta (ang. no operation). Niektórzy programiści próbują rozwiązać ten problem poprzez przypisanie wskaźnikowi do pamięci na stercie wartości ,-.. w obrębie destruktora4. ; ""/; # $ ! &.E ' , To sympatycznie wyglądający pomysł, ale nie działa zgodnie z ich intencjami. Taki wskaź- nik, który został ustawiony na zero, należy do obiektu, który to obiekt będzie usuwany w ciągu najbliższych mikrosekund. Nadal istnieje drugi wskaźnik, który wskazuje ten sam adres pamięci i mógłby zostać wyzerowany, ale nie jest dostępny dla takiego destruktora, wykonującego się przecież w odniesieniu do innego obiektu. Poza tym, nawet gdyby to zadziałało, stanowi- łoby to tylko środek zapobiegający wykonaniu błędnej instrukcji, nie powodując przecież przywrócenia pamięci, która została omyłkowo skasowana. Jak „stąd” przejść „tam”? Czy autor przestraszył czytelnika? Jeśli tak, to taka właśnie była intencja. Jeśli nie, nie szkodzi, pamiętaj jednak zawsze, by zatroszczyć się o dynamiczne zarządzanie pamięcią w swoich programach. Nawet jeśli na twoim komputerze programy działają prawidłowo, to jeszcze nie jest oczywisty dowód, że dany program jest poprawny (dodajmy to do listy naszych zasad testowania programów). 4 Taki wskaźnik przestaje wskazywać cokolwiek — przyp. tłum. 584 Część II n Programowanie obiektowe w C++ Program może działać bez żadnych zgrzytów miesiącami i latami, a następnie, np. po zain- stalowaniu w systemie jakiejś innej, zupełnie z nim nie związanej aplikacji bądź po aktualizacji systemu operacyjnego do nowszej wersji Windows™ zmieni się sposób wykorzystywania pamięci i nasz program doprowadzi do katastrofy. Taki program może także dawać nieprawi- dłowe rezultaty, które nie zostaną dostrzeżone, ponieważ działał przecież poprawnie przez wiele miesięcy, a nawet przez wiele lat. Co się wtedy dzieje? Czy przeklinać Microsoft, bo
|