Zawsze zapomnisz posprzątać, lepiej niech kompilator sprzątnie za Ciebie

Popatrzmy na taki kawałek kodu (znaleziony przeze mnie w weryfikowanym rzeczywistym ważnym programie):

    Message * msg = new Message();
    // ... trochę różnorakich przygotowań

    // Funkcja z pewnej zewnętrznej biblioteki, upycha pobrane dane do msg i zwraca status
    int result = receive_message(flags, msg);
    // Funkcja badająca status i rzucająca wyjątek w razie gdy wykryto błąd
    VerifyResult(result);

    // Funkcja która przetwarza komunikat a na koniec zwalnia zaalokowaną dlań pamięć
    Process(msg);

Jak nietrudno po chwili zastanowienia zauważyć, po każdym błędzie (a błędy były rejestrowane, po czym program kontynuował działanie) mieliśmy wyciek pamięci. Po wskazaniu mu błędu, autor chciał z miejsca go poprawić zastępując verifyResult rozwiniętym if-em zawierającym polecenie delete. Można i tak - ale nie jest to mądre (wcześniej czy później w tym kodzie pojawi się możliwość zgłoszenia innego wyjątku, jakiś warunkowy return a może break).

Znacznie mądrzejszym rozwiązaniem (zakładając, że msg musi być alokowane dynamicznie) jest wykorzystanie jakiegokolwiek wariantu sprytnego wskaźnika, np. tak:

    auto_ptr<Message> msg(new Message());
    // Dalej tak, jak było

W razie przerwania przez wyjątek naturalnej sekwencji wykonania, pamięć zostanie zwolniona w momencie zwijania stosu bieżącej funkcji. Podobnie obsłużymy return czy break czy jakiekolwiek inne zgodne z zasadami języka zakończenie bloku.

Z każdym kolejnym projektem w C++ umacniam się w przekonaniu, że dobrze napisany program nie powinien zawierać żadnej instrukcji delete! Z wyjątkiem klasy implementującej sprytne wskaźniki (standardowy auto_ptr jest trochę niemądry, lepiej używać jakiegoś 'pełnego' sprytnego wskaźnika z licznikiem referencji). Pojawiający się tu narzut może mieć znaczenie w grach 3D ale w aplikacjach biznesowych nie ma szans zostać zauważony.

komentarze obsługiwane przez Disqus