14.7 Destruktory

Prócz konstruktorów można również zdefiniować w klasie destruktor. Tak jak konstruktor jest wywoływany automatycznie podczas tworzenia obiektu, tak destruktor wywoływany jest na rzecz obiektu automatycznie podczas destrukcji tego obiektu przez system (sam destruktor obiektu nie niszczy!). Niszczenie obiektów lokalnych, utworzonych na stosie, zachodzi gdy sterowanie wychodzi z bloku, w którym były zdefiniowane. Jak wiemy, stos jest wtedy zwijany do stanu, w jakim był przy wejściu do danego bloku (funkcji); usunięte zatem zostaną wszystkie zmienne utworzone na stosie w tym bloku, wśród nich również zmienne obiektowe.

Usuwanie obiektów na stosie odbywa się w kolejności odwrotnej do ich kreowania — jako pierwsze zostaną zatem zniszczone te, które utworzone były jako ostatnie. Tak więc, na przykład, zmienne obiektowe zdefiniowane w funkcji main są niszczone po wyjściu z tej funkcji —  wtedy dopiero wywoływane są dla tych obiektów destruktory, w kolejności odwrotnej do tej, w jakiej były tworzone. Ogólnie, obiekty lokalne (na stosie) są niszczone zaraz po wyjściu sterowania z bloku, w którym były utworzone. Niszczenie obiektów zdefiniowanych globalnie zachodzi po zakończeniu wykonywania funkcji main i po zniszczeniu wszystkich obiektów lokalnych z tej funkcji, tuż przed zakończeniem wykonywania całego programu — przykład podamy w następnym podrozdziale.

Inne reguły obowiązują dla obiektów wykreowanych za pomocą operatora new na stercie, a więc nie jako obiekty lokalne. Nie są one w ogóle niszczone automatycznie; programista musi sam pamiętać, aby wywołać operator delete podając jako argument wskaźnik do obiektu na stercie; wartością tego wskaźnika musi być dokładnie ten sam adres, który został zwrócony przez operator new podczas tworzenia obiektu (patrz rozdział o zarządzaniu pamięcią ).

Destruktora można czasem w ogóle nie definiować. Jeśli go jednak definiujemy, to jego nazwą jest nazwa klasy poprzedzona znakiem tyldy: ˜Nazwa. Tak jak dla konstruktorów, definiując destruktor nie podaje się żadnego typu zwracanego, nawet void. Destruktor musi być bezparametrowy. Wynika z tego, że nie może być przeciążany. Jest to zrozumiałe: destruktor jest wywoływany automatycznie przez system podczas usuwania obiektu — system nie może wiedzieć, jakie argumenty chcielibyśmy przekazać...

Dla niektórych klas definiowanie destruktorów nie jest potrzebne. Zazwyczaj jednak napisanie destruktora jest wskazane lub nawet konieczne — kiedy takie sytuacje występują, omówimy w następnym rozdziale.

Destruktor zachowuje się jak zwykła metoda, tyle tylko, że jest wywoływany automatycznie podczas usuwania obiektu z pamięci. Samo wywołanie destruktora obiektu nie niszczy: wykonuje on to i tylko to, co zapisane jest w jego kodzie. W szczególności, jeśli chcemy, to możemy jawnie wywołać destruktor na rzecz istniejącego obiektu, na przykład

       Klasa* obiekt = new Klasa();
       // ...
       obiekt -> ~Klasa();
Takie wywołanie nie spowoduje zniszczenia obiektu; treść destruktora zostanie po prostu wykonana, tak jakby było to wywołanie zwykłej metody klasy Klasa. Zauważmy, że jawne wywołanie konstruktora na rzecz już istniejącego obiektu byłoby błędem.

T.R. Werner, 25 lutego 2017; 22:31