Cieszę się, że opublikowałeś to jako pytanie. :)
Próbowałem powiedzieć, że destruktory i finally
są koncepcyjnie różne:
- Niszczyciele służą do uwalniania zasobów ( danych )
finally
służy do powrotu do dzwoniącego ( kontrola )
Rozważmy, powiedzmy, ten hipotetyczny pseudo-kod:
try {
bar();
} finally {
logfile.print("bar has exited...");
}
finally
tutaj rozwiązuje się całkowicie problem kontroli, a nie problem zarządzania zasobami.
Nie ma sensu robić tego w destruktorze z różnych powodów:
- Nie rzeczą jest „nabyte” lub „stworzony”
- Niepowodzenie drukowania do pliku dziennika nie spowoduje wycieków zasobów, uszkodzenia danych itp. (Zakładając, że plik dziennika tutaj nie jest przekazywany z powrotem do programu w innym miejscu)
- Upadek jest uzasadniony
logfile.print
, podczas gdy zniszczenie (koncepcyjnie) nie może zawieść
Oto kolejny przykład, tym razem jak w JavaScript:
var mo_document = document, mo;
function observe(mutations) {
mo.disconnect(); // stop observing changes to prevent re-entrance
try {
/* modify stuff */
} finally {
mo.observe(mo_document); // continue observing (conceptually, this can fail)
}
}
mo = new MutationObserver(observe);
return observe();
W powyższym przykładzie ponownie nie ma żadnych zasobów do zwolnienia.
W rzeczywistości finally
blok gromadzi zasoby wewnętrznie, aby osiągnąć swój cel, co może potencjalnie zakończyć się niepowodzeniem. Dlatego nie ma sensu używać destruktora (jeśli JavaScript miał taki).
Z drugiej strony w tym przykładzie:
b = get_data();
try {
a.write(b);
} finally {
free(b);
}
finally
niszczy zasobu b
. To problem z danymi. Problem nie polega na czystym zwróceniu kontroli dzwoniącemu, ale raczej na uniknięciu wycieków zasobów.
Awaria nie jest opcją i nigdy (koncepcyjnie) nigdy nie powinna wystąpić.
Każde wydanie b
jest koniecznie połączone z akwizycją i sensowne jest użycie RAII.
Innymi słowy, tylko dlatego, że możesz użyć jednego z nich do symulacji, nie oznacza to, że oba są jednym i tym samym problemem lub że oba są odpowiednimi rozwiązaniami dla obu problemów.