Radziłbym nie używać żadnego z nich. Zamiast catchtego wyjątki, z którymi nie możesz sobie poradzić main()i po prostu returnstamtąd. Oznacza to, że masz gwarancję, że rozwijanie stosu przebiega prawidłowo i wszystkie destruktory są wywoływane. Innymi słowy:
int main(){try{// your stuff}catch(...){return1;// or whatever}}
@Neil: Zgoda w zasadzie, ale wyjątki, z którymi program nie może sobie poradzić, powinny być zgłaszane i ponownie zgłaszane. Pozwól aplikacji się zawiesić.
John Dibling
13
Aby upewnić się, że stos się rozwija, zawsze powinieneś łapać w main. Ale rzuciłbym ponownie z połowu. Ponieważ niektóre systemy operacyjne mają możliwość automatycznego wywoływania infrastruktury debugowania, jeśli została skompilowana w trybie debugowania.
Martin York
5
Wyjątki, które nie są wychwycone nawet przez program obsługi najwyższego poziomu, mogą wywołać funkcję raportowania systemu, która zrzuca proces i przesyła raport wyjątków dla uwagi programistów, taki jak raportowanie błędów systemu Windows, raporty błędów systemu Mac OS X i dzienniki błędów aplikacji na iPhone'a.
JBRWilkinson
6
@John Powodem, dla którego jest to dla mnie zaskakujące, jest to, że chociaż ma to sens w świetle tego, w jaki sposób wyjątki są faktycznie implementowane, łamie abstrakcję, że wyjątek „propaguje w górę stosu”, dopóki nie zostanie znaleziony odpowiedni program obsługi (lub zakończ nazywa). A nieszczelne abstrakcje, choć często nieuniknione, są z konieczności zaskakujące, gdy się je napotka.
abort wskazuje na „nienormalny” koniec programu i podnosi sygnał POSIX SIGABRT, co oznacza, że zostanie wywołany każdy program obsługi, który zarejestrowałeś dla tego sygnału, chociaż w każdym przypadku program i tak zakończy działanie posłów. Zwykle używałbyś abortw programie C, aby wyjść z nieoczekiwanego przypadku błędu, w którym błąd prawdopodobnie jest błędem w programie, a nie czymś w rodzaju złego wejścia lub awarii sieci. Na przykład, możesz, abortjeśli okaże się, że struktura danych ma wskaźnik NULL, a to logicznie nigdy nie powinno się zdarzyć.
exit oznacza „normalne” zakończenie programu, chociaż może to nadal oznaczać błąd (ale nie błąd). Innymi słowy, możesz exitz kodem błędu, jeśli użytkownik podał dane wejściowe, których nie można przeanalizować lub nie można odczytać pliku. Kod zakończenia równy 0 oznacza sukces. exitopcjonalnie także wywołuje programy obsługi przed zakończeniem programu. Są one zarejestrowane z funkcjami atexiti on_exit.
std :: terminate jest tym, co jest automatycznie wywoływane w programie C ++, gdy występuje nieobsługiwany wyjątek. Jest to zasadniczo odpowiednik C ++ abort, przy założeniu, że zgłaszasz wszystkie wyjątkowe błędy za pomocą wyrzucania wyjątków. To wywołuje procedurę obsługi ustawioną przez std::set_terminatefunkcję, która domyślnie po prostu wywołuje abort.
W C ++ zazwyczaj chcesz uniknąć wywołania abortlub exitbłędu, ponieważ lepiej jest wyrzucić wyjątek i pozwolić kodowi na wyższym poziomie stosu wywołań zdecydować, czy zakończenie programu jest właściwe. To, czy użyjesz exitdo sukcesu, jest kwestią okoliczności - czy sensowne jest zakończenie programu w innym miejscu niż instrukcja return w main.
std::terminatenależy traktować jako narzędzie do raportowania błędów ostatniej szansy, nawet w C ++. Problem std::terminatepolega na tym, że program obsługi terminacji nie ma dostępu do wyjątku, który nie został obsłużony, więc nie ma sposobu, aby stwierdzić, co to było. Zwykle znacznie lepiej jest zawinąć całość main w try { } catch (std::exception& ex) { }bloku. Przynajmniej wtedy możesz zgłosić więcej informacji o wyjątkach pochodzących z std::exception(chociaż oczywiście wyjątki, które nie pochodzą z std::exception, nadal byłyby nieobsłużone).
Zawijanie treści mainin try { } catch(...) { }nie jest dużo lepsze niż ustawienie procedury obsługi zakończenia, ponieważ znowu nie masz dostępu do danego wyjątku. Edycja: odpowiedź według Neila Butterwortha, jest korzyść w tym, że w tym przypadku stos jest rozwijany, co (nieco zaskakujące) nie jest prawdziwe w przypadku nieobsługiwanego wyjątku.
Czy możesz zaktualizować tę odpowiedź za pomocą informacji C ++ 11? Wygląda na to, że są teraz sposoby na uzyskanie wyjątku w przechwytywaniu (...) i w module obsługi terminacji.
Nie ma znaczenia, że możesz uzyskać bieżący wyjątek, ponieważ nie możesz go sprawdzić. Wszystko, co możesz zrobić, to rzucić go ponownie.
seattlecpp
2
@seattlecpp, możesz go ponownie wrzucić i złapać odniesienie do niego, które możesz następnie sprawdzić
gpeche
16
std :: abort i std :: exit (i więcej: std :: _ Exit, std :: quick_exit) to tylko funkcje niższego poziomu. Używasz ich, aby powiedzieć programowi, co dokładnie chcesz, aby zrobił: jakie destruktory (i czy) ma wywołać, jakie inne funkcje czyszczące mają wywołać, jaką wartość zwrócić itp.
std :: terminate jest abstrakcją wyższego poziomu: jest wywoływana (przez środowisko wykonawcze lub przez ciebie) w celu wskazania, że wystąpił błąd w programie i że z jakiegoś powodu nie można go obsłużyć przez zgłoszenie wyjątku. Konieczność tego zwykle pojawia się, gdy błąd pojawia się w samym mechanizmie wyjątków, ale możesz go użyć w dowolnym momencie, gdy nie chcesz, aby program kontynuował pracę poza podanym błędem. Skompilowałem pełną listę sytuacji, w których std :: terminate jest wywoływane w moim poście. Nie jest określone, co robi std :: terminate, ponieważ masz nad nim kontrolę. Możesz skonfigurować zachowanie, rejestrując dowolne funkcje. Ograniczenia, które masz, są takie, że funkcja nie może wrócić do miejsca błędu i nie może wyjść przez wyjątek, ale technicznie możesz nawet uruchomić pompę wiadomości w środku. Aby zobaczyć listę przydatnych rzeczy, które możesz zrobić w środku, zobacz mój drugi wpis .
W szczególności zwróć uwagę, że std :: terminate jest uważane za procedurę obsługi wyjątków w kontekstach, w których std :: terminate jest wywoływana z powodu wyrzuconego wyjątku, którego nie można obsłużyć, i możesz sprawdzić, jaki był wyjątek i sprawdzić go za pomocą C ++ 11 przy użyciu std :: rethrow_exception i std :: current_exception. To wszystko jest w moim poście .
Czy warto mieć program obsługi czyszczenia na wypadek, gdyby program zakończył działanie z powodu sygnałów systemowych? Na przykład nieprawidłowy dostęp do pamięci prowadzi do wygenerowania sygnału SIGSEGV. W takim przypadku, czy dobrze jest po prostu pozwolić programowi na zakończenie pracy i mieć plik core lub zarejestrować procedurę obsługi sygnału, aby wykonać czyszczenie? Czy są jakieś obawy związane z czyszczeniem podczas obsługi sygnałów systemowych w porównaniu do czyszczenia podczas obsługi std :: terminate?
Jeśli twój program jest wielowątkowy, wywołanie exit()najprawdopodobniej spowoduje awarię, ponieważ globalne / statyczne std::threadobiekty będą próbowały zniszczyć bez opuszczania ich wątków.
Jeśli chcesz zwrócić kod błędu i wyjść z programu (mniej więcej) normalnie, wywołaj quick_exit()programy wielowątkowe. W przypadku nieprawidłowego zakończenia (bez możliwości określenia kodu błędu) abort()lub std::terminate()można zadzwonić.
terminate () jest wywoływana automatycznie, gdy wystąpi wyjątek, którego nie można obsłużyć. Domyślnie terminate () wywołuje abort (). Możesz ustawić niestandardowy uchwyt za pomocą funkcji set_terminate ().
abort () wysyła sygnał SIGABRT.
exit () niekoniecznie jest złą rzeczą. Pomyślnie wychodzi z aplikacji i wywołuje funkcje atexit () w kolejności LIFO. Zwykle nie widzę tego w aplikacjach C ++, jednak widzę to w wielu aplikacjach opartych na Uniksie, w których na końcu wysyła kod zakończenia. Zwykle wyjście (0) oznacza pomyślne uruchomienie aplikacji.
Nieudany! Zarówno w systemie Unix, jak i DOS, exit (0) oznacza sukces, a każda inna wartość przekazana do exit () wskazuje na niepowodzenie, a nie na odwrót!
std::abort
jest rozsądne, jeśli wyjątek nie może zostać rozwiązany w destruktorze.std::terminate
tych artykułów można znaleźć w znakomitym blogu Andrzeja poświęconym C ++: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminateOdpowiedzi:
Radziłbym nie używać żadnego z nich. Zamiast
catch
tego wyjątki, z którymi nie możesz sobie poradzićmain()
i po prostureturn
stamtąd. Oznacza to, że masz gwarancję, że rozwijanie stosu przebiega prawidłowo i wszystkie destruktory są wywoływane. Innymi słowy:źródło
abort wskazuje na „nienormalny” koniec programu i podnosi sygnał POSIX SIGABRT, co oznacza, że zostanie wywołany każdy program obsługi, który zarejestrowałeś dla tego sygnału, chociaż w każdym przypadku program i tak zakończy działanie posłów. Zwykle używałbyś
abort
w programie C, aby wyjść z nieoczekiwanego przypadku błędu, w którym błąd prawdopodobnie jest błędem w programie, a nie czymś w rodzaju złego wejścia lub awarii sieci. Na przykład, możesz,abort
jeśli okaże się, że struktura danych ma wskaźnik NULL, a to logicznie nigdy nie powinno się zdarzyć.exit oznacza „normalne” zakończenie programu, chociaż może to nadal oznaczać błąd (ale nie błąd). Innymi słowy, możesz
exit
z kodem błędu, jeśli użytkownik podał dane wejściowe, których nie można przeanalizować lub nie można odczytać pliku. Kod zakończenia równy 0 oznacza sukces.exit
opcjonalnie także wywołuje programy obsługi przed zakończeniem programu. Są one zarejestrowane z funkcjamiatexit
ion_exit
.std :: terminate jest tym, co jest automatycznie wywoływane w programie C ++, gdy występuje nieobsługiwany wyjątek. Jest to zasadniczo odpowiednik C ++
abort
, przy założeniu, że zgłaszasz wszystkie wyjątkowe błędy za pomocą wyrzucania wyjątków. To wywołuje procedurę obsługi ustawioną przezstd::set_terminate
funkcję, która domyślnie po prostu wywołujeabort
.W C ++ zazwyczaj chcesz uniknąć wywołania
abort
lubexit
błędu, ponieważ lepiej jest wyrzucić wyjątek i pozwolić kodowi na wyższym poziomie stosu wywołań zdecydować, czy zakończenie programu jest właściwe. To, czy użyjeszexit
do sukcesu, jest kwestią okoliczności - czy sensowne jest zakończenie programu w innym miejscu niż instrukcja return wmain
.std::terminate
należy traktować jako narzędzie do raportowania błędów ostatniej szansy, nawet w C ++. Problemstd::terminate
polega na tym, że program obsługi terminacji nie ma dostępu do wyjątku, który nie został obsłużony, więc nie ma sposobu, aby stwierdzić, co to było. Zwykle znacznie lepiej jest zawinąć całość main wtry { } catch (std::exception& ex) { }
bloku. Przynajmniej wtedy możesz zgłosić więcej informacji o wyjątkach pochodzących zstd::exception
(chociaż oczywiście wyjątki, które nie pochodzą zstd::exception
, nadal byłyby nieobsłużone).Zawijanie treści
main
intry { } catch(...) { }
nie jest dużo lepsze niż ustawienie procedury obsługi zakończenia, ponieważ znowu nie masz dostępu do danego wyjątku. Edycja: odpowiedź według Neila Butterwortha, jest korzyść w tym, że w tym przypadku stos jest rozwijany, co (nieco zaskakujące) nie jest prawdziwe w przypadku nieobsługiwanego wyjątku.źródło
std::current_exception()
. Zobacz przykład tutaj: akrzemi1.wordpress.com/2011/10/05/using-stdterminatestd :: abort i std :: exit (i więcej: std :: _ Exit, std :: quick_exit) to tylko funkcje niższego poziomu. Używasz ich, aby powiedzieć programowi, co dokładnie chcesz, aby zrobił: jakie destruktory (i czy) ma wywołać, jakie inne funkcje czyszczące mają wywołać, jaką wartość zwrócić itp.
std :: terminate jest abstrakcją wyższego poziomu: jest wywoływana (przez środowisko wykonawcze lub przez ciebie) w celu wskazania, że wystąpił błąd w programie i że z jakiegoś powodu nie można go obsłużyć przez zgłoszenie wyjątku. Konieczność tego zwykle pojawia się, gdy błąd pojawia się w samym mechanizmie wyjątków, ale możesz go użyć w dowolnym momencie, gdy nie chcesz, aby program kontynuował pracę poza podanym błędem. Skompilowałem pełną listę sytuacji, w których std :: terminate jest wywoływane w moim poście. Nie jest określone, co robi std :: terminate, ponieważ masz nad nim kontrolę. Możesz skonfigurować zachowanie, rejestrując dowolne funkcje. Ograniczenia, które masz, są takie, że funkcja nie może wrócić do miejsca błędu i nie może wyjść przez wyjątek, ale technicznie możesz nawet uruchomić pompę wiadomości w środku. Aby zobaczyć listę przydatnych rzeczy, które możesz zrobić w środku, zobacz mój drugi wpis .
W szczególności zwróć uwagę, że std :: terminate jest uważane za procedurę obsługi wyjątków w kontekstach, w których std :: terminate jest wywoływana z powodu wyrzuconego wyjątku, którego nie można obsłużyć, i możesz sprawdzić, jaki był wyjątek i sprawdzić go za pomocą C ++ 11 przy użyciu std :: rethrow_exception i std :: current_exception. To wszystko jest w moim poście .
źródło
quick_exit () !
Jeśli twój program jest wielowątkowy, wywołanie
exit()
najprawdopodobniej spowoduje awarię, ponieważ globalne / statycznestd::thread
obiekty będą próbowały zniszczyć bez opuszczania ich wątków.Jeśli chcesz zwrócić kod błędu i wyjść z programu (mniej więcej) normalnie, wywołaj
quick_exit()
programy wielowątkowe. W przypadku nieprawidłowego zakończenia (bez możliwości określenia kodu błędu)abort()
lubstd::terminate()
można zadzwonić.Uwaga: funkcja quick_exit () nie była obsługiwana przez MSVC ++ do wersji 2015.
źródło
źródło
terminate () jest wywoływana automatycznie, gdy wystąpi wyjątek, którego nie można obsłużyć. Domyślnie terminate () wywołuje abort (). Możesz ustawić niestandardowy uchwyt za pomocą funkcji set_terminate ().
abort () wysyła sygnał SIGABRT.
exit () niekoniecznie jest złą rzeczą. Pomyślnie wychodzi z aplikacji i wywołuje funkcje atexit () w kolejności LIFO. Zwykle nie widzę tego w aplikacjach C ++, jednak widzę to w wielu aplikacjach opartych na Uniksie, w których na końcu wysyła kod zakończenia. Zwykle wyjście (0) oznacza pomyślne uruchomienie aplikacji.
źródło