Bolączki C++ #3 – RAII vs finally

2007-09-13 9:23

Wyjątki są sposobem na zasygnalizowanie nietypowych i niespodziewanych błędów, które poważnie zaburzają działanie programu. I właśnie to, że potencjalnie wyjątek może wystąpić w bardzo wielu miejscach w kodzie, rodzi pewne kłopoty. Problemami są chociażby zasoby: coś, co się pozyskuje, wykorzystuje, a następnie zwalnia, gdyż w przeciwnym razie doszłoby do wycieku. Typowym zasobem jest chociażby dynamicznie alokowana pamięć – jeżeli jej nie zwolnimy, nastąpi klasyczny wyciek, jako że C++ nie posiada garbage collectora, który mógłby się tym zająć za nas.

W C++ zalecanym rozwiązaniem tego problemu jest technika znana jako RAII (Resource Acquision Is Initialization – pozyskanie zasobu jest inicjalizacją). Korzysta ona z faktu, że w naszym ulubionym języku programowania możemy tworzyć obiekty lokalne z konstruktorami i destruktorami. Te drugie wywołają się zawsze przy opuszczaniu danego bloku kodu – niezależnie od tego, czy stało się z powodu wyjątku czy tez normalnego przebiegu programu. Pomysł polega więc na tym, by tworzyć obiekt specjalnie przygotowanej klasy w momencie pozyskania zasobu, zaś destruktor tego obiektu zajmie się już jego zwolnieniem, niezależnie od powodu.

  1. try
  2. {
  3.    // 'wskaźnik lokalny' - chroni przed wyciekiem pamięci
  4.    std::auto_ptr<CFoo> pFoo(new CFoo(...));i
  5.  
  6.    // strumień plikowy - automatycznie zamyka otwarty plik
  7.    std::fstream FS("file.txt", std::ios::out);
  8. }
  9. catch (...) { /* ... */ }

Dopóki korzystamy z pamięci albo z plików, wszystko jest w porządku; odpowiednie klasy (jak auto_ptr) posiada bowiem Biblioteka Standardowa. Gorzej jeśli chcemy skorzystać z innego rodzaju zasobów. Jeśli odpowiednia klasa realizująca technikę RAII nie istnieje, nie pozostaje nam nic innego, jak samemu ją sobie zapewnić (czytaj: napisać). I tak dla każdego rodzaju niestandardowych zasobów, które używamy. Po niedługim czasie można by z tych klas ułożyć własną “bibliotekę standardową” ;)

Alternatywą dla RAII jest dodanie trzeciego bloku (po try i catch) do konstrukcji łapiącej wyjątki. Jest on zwykle nazywany finally. Instrukcje zawarte w tym bloku są wykonywane zawsze po tych z bloku try – niezależnie od tego czy wyjątek wystąpił czy nie. Jest to więc bardzo dobre miejsce na wszelki kod zwalniający pozyskane wcześniej zasoby, np.:

  1. import java.io.*;
  2.  
  3. try
  4. {
  5.    FileReader fr = new FileReader("file.txt");
  6.  
  7.    // (czytanie pliku)
  8. }
  9. finally { fr.close(); }

Co ciekawe, posiadają go języki, które jeden z najważniejszych zasobów – pamięć – mają zarządzaną przez odśmiecacz, który praktycznie wyklucza możliwość powstania wycieków. Rzecz jednak w tym, że niektóre zasoby, jak chociażby otwarte pliku, nie mogą sobie czekać na to, aż odśmiecacz przypomni sobie o nich, gdyż wtedy byłyby blokowane stanowczo zbyt długo.

Czy C++ też potrzebuje instrukcji finally? Na pewno nie jest to bardzo paląca potrzeba, jako że technika RAII zapewnia komplet potrzebnej tutaj funkcjonalności. To drugie, alternatywne rozwiązanie ma jednak szereg zalet:

  • Brak konieczności opakowywania każdego wykorzystywanego zasobu w specjalną klasę.
  • Większa przejrzystość kodu, w którym zarówno operację pozyskania, jak i zwolnienia zasobu.
  • finally można też pożytecznie wykorzystać nawet wówczas, gdy w grę nie wchodzi możliwość pojawienia się wyjątku. Jeżeli na przykład w jakiejś skomplikowanej funkcji mamy wiele miejsc, w których może nastąpić jej zakończenie, a przy każdej okazji może być potrzeba wykonania jeszcze jakichś czynności końcowych. Wtedy moglibyśmy zamknąć całą treść funkcji w try, a owe czynności umieścić w sekcji finally. Trzeba by się było jednak liczyć z tym, że blok try nie jest darmowy i jego użyciu nakłada pewien narzut.

I przede wszystkim: RAII i finally nie wykluczają się nawzajem. Dlatego obecność tego drugiego mechanizmu w C++ na pewno by nam nie zaszkodziła :)

Tags: , ,
Author: Xion, posted under Programming »



Adding comments is disabled.

Comments are disabled.
 


© 2018 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.