Chciałbym, aby mój kod C ++ przestał działać, jeśli zostanie spełniony określony warunek, ale nie jestem pewien, jak to zrobić. Więc w dowolnym momencie, jeśli if
instrukcja jest prawdziwa, zakończ kod w następujący sposób:
if (x==1)
{
kill code;
}
main()
użyciu return, w funkcjach użyj poprawnej wartości zwracanej lub wygeneruj odpowiedni wyjątek. Nie używajexit()
!NDEBUG
zdefiniowanym,assert
prawdopodobnie stanie się brakiem op, więc nie powinieneś polegać na nim więcej niż na wykrywaniu błędów.return
frommain()
jest całkowicie logiczne. Ustawia kod wyjścia zgłaszany przez program do systemu operacyjnego, co może być przydatne w wielu przypadkach (na przykład, jeśli program może być używany jako część większego procesu).exit()
uważane za złe? - SO 25141737 i Jak wyjść z programu C ++? - SO 1116493 są teraz zamknięte jako duplikaty tego. Pierwszy z nich zawiera odniesienie do „oprogramowania tylko do awarii”, które może być warte spojrzenia, choćby po to, aby pobudzić twoje myślenie o tym, jak napisać solidne oprogramowanie.Odpowiedzi:
Istnieje kilka sposobów, ale najpierw musisz zrozumieć, dlaczego czyszczenie obiektów jest ważne, a zatem powód
std::exit
jest marginalizowany wśród programistów C ++.RAII i rozwijanie stosu
C ++ korzysta z idiomu zwanego RAII , co w uproszczeniu oznacza, że obiekty powinny wykonywać inicjalizację w konstruktorze i czyszczenie w destruktorze. Na przykład
std::ofstream
klasa [może] otworzyć plik podczas konstruktora, a następnie użytkownik wykonuje na nim operacje wyjściowe, a na końcu pod koniec swojego cyklu życia, zwykle określanego przez jego zakres, wywoływany jest destruktor, który zasadniczo zamyka plik i opróżnia dowolna zapisana zawartość na dysk.Co się stanie, jeśli nie dojdziesz do destruktora, aby opróżnić i zamknąć plik? Kto wie! Ale możliwe, że nie zapisze wszystkich danych, które miał zapisać w pliku.
Na przykład rozważ ten kod
W każdej możliwości dzieje się:
os
ten sposób wywołując swój destruktor i wykonując prawidłowe czyszczenie przez zamknięcie i opróżnienie pliku na dysk.inner_mad
, odwijak przejdzie przez stosmad
imain
aby wykonać prawidłowe czyszczenie, wszystkie obiekty zostaną zniszczone poprawnie, w tymptr
ios
.exit
jest funkcją C i nie jest świadomy ani zgodny z idiomami C ++. To nie wykonywać porządki na swoich obiektach, w tymos
w tym samym zakresie. Dlatego Twój plik nie zostanie poprawnie zamknięty i z tego powodu treść może nigdy nie zostać w nim zapisana!return 0
a tym samym dając taki sam efekt jak możliwość 1, tj. Prawidłowe czyszczenie.Ale nie bądźcie tak pewni tego, co wam powiedziałem (głównie możliwości 2 i 3); kontynuuj czytanie, a dowiemy się, jak przeprowadzić prawidłowe czyszczenie oparte na wyjątkach.
Możliwe sposoby zakończenia
Wróć z głównego!
Powinieneś to zrobić, gdy tylko jest to możliwe; zawsze wolisz wracać ze swojego programu, zwracając odpowiedni status wyjścia z głównego.
Program wywołujący Twój program i być może system operacyjny mogą chcieć wiedzieć, czy to, co program miał zrobić, zostało wykonane pomyślnie, czy nie. Z tego samego powodu powinieneś zwrócić zero lub
EXIT_SUCCESS
aby zasygnalizować, że program został pomyślnie zakończony iEXIT_FAILURE
aby zasygnalizować, że program zakończył się niepowodzeniem, każda inna forma wartości zwracanej jest zdefiniowana w implementacji ( § 18.5 / 8 ).Jednak możesz być bardzo głęboko w stosie wywołań, a zwracanie wszystkiego może być bolesne ...
[Nie] zgłaszaj wyjątku
Zgłoszenie wyjątku wykona prawidłowe czyszczenie obiektu za pomocą rozwijania stosu, wywołując destruktor każdego obiektu w dowolnym poprzednim zakresie.
Ale oto haczyk ! Jest zdefiniowane w implementacji, czy odwijanie stosu jest wykonywane, gdy zgłoszony wyjątek nie jest obsługiwany (przez klauzulę catch (...)), a nawet jeśli masz
noexcept
funkcję w środku stosu wywołań. Jest to określone w §15.5.1 [z wyjątkiem terminów] :Więc musimy to złapać!
Rzuć wyjątek i złap go w pozycji głównej!
Ponieważ niewyłapane wyjątki mogą nie powodować rozwijania stosu (a tym samym nie będą wykonywać właściwego czyszczenia) , powinniśmy wychwycić wyjątek w main, a następnie zwrócić status wyjścia (
EXIT_SUCCESS
lubEXIT_FAILURE
).Prawdopodobnie dobrym rozwiązaniem byłoby:
[Nie] std :: exit
Nie powoduje to żadnego rozwijania stosu i żaden żywy obiekt na stosie nie wywoła odpowiedniego niszczyciela w celu przeprowadzenia czyszczenia.
Jest to egzekwowane w §3.6.1 / 4 [basic.start.init] :
Pomyśl o tym teraz, dlaczego zrobiłbyś coś takiego? Ile przedmiotów boleśnie uszkodziłeś?
Inne [złe] alternatywy
Istnieją inne sposoby zakończenia programu (inne niż zawieszanie się) , ale nie są zalecane. Dla wyjaśnienia zostaną tutaj przedstawione. Zwróć uwagę, że normalne zakończenie programu nie oznacza rozwijania stosu, ale dobry stan systemu operacyjnego.
std::_Exit
powoduje normalne zakończenie programu i to wszystko.std::quick_exit
powoduje normalne zakończenie programu i wywołujestd::at_quick_exit
procedury obsługi, żadne inne czyszczenie nie jest wykonywane.std::exit
powoduje normalne zakończenie programu, a następnie wywołuje programystd::atexit
obsługi. Przeprowadzane są inne rodzaje porządków, takie jak wywoływanie destrukcyjnych obiektów statycznych.std::abort
powoduje nieprawidłowe zakończenie programu, żadne czyszczenie nie jest wykonywane. Należy to wywołać, jeśli program zakończy się w naprawdę, naprawdę nieoczekiwany sposób. Nie zrobi nic poza zasygnalizowaniem systemu operacyjnego o nienormalnym zakończeniu. W tym przypadku niektóre systemy wykonują zrzut pamięci.std::terminate
wywołuje te,std::terminate_handler
którestd::abort
domyślnie wywołują .źródło
new
rzucastd::bad_alloc
i program zapomnieli złapać tego nigdzie wyjątku) będzie nie prawidłowo rozwijać stosu przed zakończeniem. Wydaje się to głupie mnie byłoby łatwo zasadniczo zawinąć wywołaniemain
w trywialnytry{
-}catch(...){}
bloku, który zapewniłby stos odwijania jest prawidłowo wykonane w takich przypadkach, bez ponoszenia kosztów (przez co rozumiem: programy nie to za pomocą zapłaci nie rzut karny). Czy jest jakiś konkretny powód, dla którego tego nie zrobiono?std::abort
aby system operacyjny mógł zwolnić całą pamięć, gniazda i deskryptory plików bez zamiany na minutę.Jak wspomniał Martin York, wyjście nie wykonuje koniecznego czyszczenia, tak jak robi to return.
Zawsze lepiej jest użyć powrotu w miejscu wyjścia. Jeśli nie jesteś w Main, gdziekolwiek chcesz wyjść z programu, wróć najpierw do Main.
Rozważ poniższy przykład. W następującym programie zostanie utworzony plik z wymienioną zawartością. Ale jeśli komentowany jest return i niekomentowane wyjście (0), kompilator nie zapewnia, że plik będzie zawierał wymagany tekst.
Nie tylko to, posiadanie wielu punktów wyjścia w programie utrudni debugowanie. Wyjścia używaj tylko wtedy, gdy jest to uzasadnione.
źródło
return (EXIT_SUCCESS);
zamiast tegoreturn(0)
Wywołaj
std::exit
funkcję.źródło
exit()
gdy kod biblioteki działa w procesie hosta - ten ostatni zakończy działanie w szczerym polu.Ludzie mówią „call exit (kod powrotu)”, ale to zła forma. W małych programach jest w porządku, ale jest z tym wiele problemów:
Naprawdę, jedynym wyjściem z problemu jest wyjście z tego wiersza w main.cpp:
Jeśli używasz metody exit () do obsługi błędów, powinieneś dowiedzieć się o wyjątkach (i zagnieżdżaniu wyjątków), jako o wiele bardziej eleganckiej i bezpiecznej metodzie.
źródło
return 0;
umieść to w dowolnym miejscu,int main()
a program natychmiast się zamknie.źródło
main
.int main()
jest inaczej. Program używaint main()
do rozpoczynania i kończenia. Nie ma innego sposobu, aby to zrobić. Program będzie działał, dopóki nie zwrócisz true lub false. Niemal przez cały czas zwracana jest wartość 1 lub true, aby wskazać, że program został poprawnie zamknięty (np. Może użyć wartości false, jeśli nie można zwolnić pamięci lub z dowolnego powodu). Pozwolenie na uruchomienie programu jest zawsze złym pomysłem, przykład:int main() { int x = 2; int foo = x*5; std::cout << "blah"; }
int main
; jednak nie zawsze dobrym pomysłem jest posiadanie wielu instrukcji return w głównej procedurze. Jednym potencjalnym zastrzeżeniem jest poprawa czytelności kodu; jednak użycie czegoś w rodzaju flagi logicznej, która zmienia stan, aby uniemożliwić działanie niektórych sekcji kodu w tym stanie aplikacji. Osobiście uważam, że wspólne wyrażenie zwrotne sprawia, że większość aplikacji jest bardziej czytelna. Na koniec pytania dotyczące czyszczenia pamięci i prawidłowego zamykania obiektów we / wy przed wyjściem.true
zmain
wskazać prawidłowe rozwiązanie. Musisz zwrócić zero lubEXIT_SUCCESS
(lub, jeśli chceszfalse
, które domyślnie zostałyby przekonwertowane na zero), aby wskazać normalne zakończenie. Aby wskazać awarię, możesz wrócićEXIT_FAILURE
. Każde inne znaczenie kodu jest zdefiniowane w implementacji (w systemach POSIX oznaczałoby to rzeczywisty kod błędu).Program zakończy się, gdy przepływ wykonania osiągnie koniec głównej funkcji.
Aby go wcześniej zakończyć, możesz użyć funkcji exit (int status), gdzie status jest wartością zwracaną do tego, co uruchomiło program. 0 zwykle oznacza stan bez błędu
źródło
Albo zwróć wartość z twojego,
main
albo użyjexit
funkcji. Oba przyjmują int. Tak naprawdę nie ma znaczenia, jaką wartość zwracasz, chyba że masz zewnętrzny proces sprawdzający wartość zwracaną.źródło
Jeśli masz błąd gdzieś głęboko w kodzie, albo rzuć wyjątek, albo ustaw kod błędu. Zawsze lepiej jest wyrzucić wyjątek niż ustawić kody błędów.
źródło
Zasadniczo użyłbyś tej
exit()
metody z odpowiednim statusem wyjścia .Zero oznaczałoby udany bieg. Niezerowy status wskazuje, że wystąpił jakiś problem. Ten kod zakończenia jest używany przez procesy nadrzędne (np. Skrypty powłoki) w celu ustalenia, czy proces został pomyślnie uruchomiony.
źródło
return 0;
. Zakładam, żeexit()
jest podobnyassert(false);
i dlatego powinien być używany tylko w fazie rozwojowej do wczesnych problemów połowowych.Poza wywoływaniem exit (kod_błędu) - który wywołuje programy obsługi atexit, ale nie destruktory RAII itp. - coraz częściej używam wyjątków.
Coraz bardziej wygląda mój główny program
gdzie minor_main w miejscu, gdzie wszystkie rzeczy, które zostały pierwotnie umieszczone - tj. pierwotny main jest przemianowany na wtórny_main i dodawany jest kod główny powyżej. Jest to po prostu drobiazg, aby nie było za dużo kodu między szufladą a połowem w głównym.
Jeśli chcesz, złap inne typy wyjątków.
Bardzo lubię łapać typy błędów łańcucha, takie jak std :: string lub char *, i drukować je w module obsługi catch w main.
Korzystanie z takich wyjątków pozwala przynajmniej wywoływać destruktory RAII, aby mogli wykonać czyszczenie. Co może być przyjemne i przydatne.
Ogólnie rzecz biorąc, obsługa błędów C - wyjście i sygnały - oraz obsługa błędów C ++ - wyjątki try / catch / throw - w najlepszym przypadku grają niespójnie.
Następnie wykrywasz błąd
lub jakiś bardziej szczegółowy typ wyjątku.
źródło
exit
w twoim programie. Skoro jesteś w środkumain
, możesz po prostureturn exitCode;
.Jeśli instrukcja if znajduje się w pętli, możesz jej użyć
Jeśli chcesz uciec trochę kodu i kontynuować pętlę Użyj:
kontyntynuj;
Jeśli instrukcja if nie znajduje się w pętli, możesz użyć:
źródło
Koleś ...
exit()
funkcja jest zdefiniowana w stdlib.hMusisz więc dodać preprocesor.
Umieść
include stdlib.h
w sekcji nagłówkaNastępnie używaj
exit();
gdziekolwiek chcesz, ale pamiętaj, aby wstawić liczbę interger w nawiasie wyjścia.na przykład:
źródło
Jeśli warunek, który testuję, jest naprawdę złą wiadomością, robię to:
To daje mi niezły rdzeń, z którego mogę zbadać sytuację.
źródło
Aby przełamać warunek, użyj return (0);
W twoim przypadku byłoby to:
źródło