Po pierwsze, dobrze wiem, dlaczego w C ++ nie ma konstrukcji „nareszcie”? ale długotrwała dyskusja na temat komentarza wydaje się uzasadniać osobne pytanie.
Oprócz problemu, że finally
w języku C # i Javie może istnieć tylko raz (== 1) na zakres, a pojedynczy zakres może mieć wiele (== n) destrukterów C ++, myślę, że są one zasadniczo takie same. (Z pewnymi różnicami technicznymi.)
Jednak inny użytkownik argumentował :
... Próbowałem powiedzieć, że dtor jest z natury narzędziem (Release sematics), a na koniec z natury narzędziem (Commit semantyka). Jeśli nie rozumiesz, dlaczego: zastanów się, dlaczego uzasadnione jest umieszczanie wyjątków jeden na drugim w ostatecznie blokach i dlaczego to samo nie dotyczy niszczycieli. (W pewnym sensie jest to kwestia kontroli danych i danych. Destruktory służą do uwalniania danych, wreszcie do kontroli. Są różne; szkoda, że C ++ je łączy).
Czy ktoś może to wyjaśnić?
źródło
A
iB
. Jeśli jeden wątek wyrzuci, wycofanieA's
transakcji nie powinno zniszczyć przydzielonych zasobówB
, np. - stany wątku są od siebie niezależne, a pamięć trwała na stercie jest niezależna od obu. Jednak zazwyczaj w C ++ pamięć sterty jest nadal powiązana z obiektami na stosie.std::vector
obiekt może znajdować się na stosie, ale wskazywać na pamięć na stercie - zarówno obiekt wektorowy (na stosie), jak i jego zawartość (na stercie) zostaną w tym przypadku zwolnione podczas cofania stosu, ponieważ zniszczenie wektora na stosie wywołałoby destruktor, który zwolni powiązaną pamięć na stercie (i podobnie zniszczy te elementy stosu). Zazwyczaj w celu zapewnienia bezpieczeństwa wyjątków większość obiektów C ++ żyje na stosie, nawet jeśli są one tylko uchwytami wskazującymi pamięć na stercie, automatyzując proces zwalniania pamięci stosu i stosu podczas rozwijania stosu.Cieszę się, że opublikowałeś to jako pytanie. :)
Próbowałem powiedzieć, że destruktory i
finally
są koncepcyjnie różne:finally
służy do powrotu do dzwoniącego ( kontrola )Rozważmy, powiedzmy, ten hipotetyczny pseudo-kod:
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:
logfile.print
, podczas gdy zniszczenie (koncepcyjnie) nie może zawieśćOto kolejny przykład, tym razem jak w JavaScript:
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:
finally
niszczy zasobub
. 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.
źródło
finally
jest najczęściej wykorzystywany do zwalniania zasobów (innych niż pamięć)?finally
.finally
klauzuli. Światopogląd C ++ wprowadziłby klasę zarządzającą tym „zasobem” przypisania do zmiennej pseudo-globalnej. Co to ma sens konceptualny? Ale destruktory są młotem C ++ wymaganym do wykonania kodu końca bloku.Odpowiedź k3b naprawdę dobrze to wyraża:
Jeśli chodzi o „zasoby”, chciałbym odnieść się do Jona Kalba: RAII powinno oznaczać, że przejęcie odpowiedzialności to inicjalizacja .
W każdym razie, jeśli chodzi o domniemane vs. jawne, wydaje się, że tak naprawdę jest:
Myślę, że to tyle w części konceptualnej ...
... teraz jest kilka interesujących szczegółów IMHO:
fault
bloku do uruchomienia kodu, który powinien być uruchomiony tylko w przypadku wyjątkowego wyjścia z zakresu.SCOPE_EXIT
,SCOPE_FAIL
aSCOPE_SUCCESS
w bibliotece głupoty . Zobacz Andrei Alexandrescu: Obsługa błędów w C ++ / deklaratywnym przepływie kontroli (odbędzie się w NDC 2014 )Nie sądzę też, aby c'tor / d'tor musiał koncepcyjnie „zdobywać” lub „tworzyć” cokolwiek, poza odpowiedzialnością za uruchomienie kodu w destruktorze. Co ostatecznie robi również: uruchom jakiś kod.
I chociaż kod w końcu może z pewnością rzucić wyjątek, nie jest to dla mnie wystarczające rozróżnienie, aby powiedzieć, że są one koncepcyjnie różne od jawnych do niejawnych.
(Plus, wcale nie jestem przekonany, że „dobry” kod powinien w końcu rzucić - może to kolejne pytanie do siebie.)
źródło
observer
przykładzie, że rzucanie byłoby naprawdę złym pomysłem.) Jeśli chcesz porozmawiać o tym dalej, możesz otworzyć czat. Na pewno fajnie było myśleć o twoich argumentach. Twoje zdrowie.