Chociaż zgadzam się, że łapanie ...
bez ponownego rzucania jest rzeczywiście złe, uważam jednak, że przy użyciu takich konstrukcji:
try
{
// Stuff
}
catch (...)
{
// Some cleanup
throw;
}
Jest akceptowalny w przypadkach, w których RAII nie ma zastosowania . (Proszę, nie pytaj ... nie wszyscy w mojej firmie lubią programowanie obiektowe, a RAII jest często postrzegane jako „bezużyteczne rzeczy szkolne” ...)
Moi współpracownicy mówią, że zawsze powinieneś wiedzieć, jakie wyjątki mają być zgłaszane i że zawsze możesz używać konstrukcji takich jak:
try
{
// Stuff
}
catch (exception_type1&)
{
// Some cleanup
throw;
}
catch (exception_type2&)
{
// Some cleanup
throw;
}
catch (exception_type3&)
{
// Some cleanup
throw;
}
Czy istnieje dobrze przyjęta dobra praktyka w tych sytuacjach?
...
”, a moje pytanie nacisk na „Powinienem lepiej złapać...
lub<specific exception>
przed Ponowne generowanie”main
,catch(...) { return EXIT_FAILURE; }
może mieć rację w kodzie, który nie działa w debuggerze. Jeśli nie złapiesz, stos może się nie rozwinąć. Tylko wtedy, gdy twój debugger wykryje niewyłapane wyjątki, chcesz, aby odeszłymain
.Odpowiedzi:
Twój współpracownik, chciałbym to powiedzieć, najwyraźniej nigdy nie pracował nad bibliotekami ogólnego przeznaczenia.
Jak, u licha, klasa może
std::vector
nawet udawać, że wie, co rzucą konstruktorzy kopii, jednocześnie gwarantując wyjątkowe bezpieczeństwo?Gdybyście zawsze wiedzieli, co robi callee w czasie kompilacji, polimorfizm byłby bezużyteczny! Czasami głównym celem jest abstrahowanie od tego, co dzieje się na niższym poziomie, więc nie chcesz wiedzieć, co się dzieje!
źródło
...
?To, co wydaje się, że Cię złapano, to piekło kogoś, kto próbuje zjeść swoje ciasto i je zjeść.
RAII i wyjątki są zaprojektowane tak, aby iść w parze. RAII to sposób, w jaki nie trzeba pisać wielu
catch(...)
instrukcji, aby wykonać czyszczenie. Oczywiście stanie się to automatycznie. Wyjątki są jedynym sposobem pracy z obiektami RAII, ponieważ konstruktory mogą odnieść sukces lub wyrzucić (lub wprowadzić obiekt w stan błędu, ale kto tego chce?).catch
Oświadczenie może zrobić jedną z dwóch rzeczy: obsługiwać błąd lub wyjątkową okoliczność, czy prac porządkowych. Czasami robi to obie, ale każdecatch
oświadczenie istnieje, aby wykonać przynajmniej jedną z nich.catch(...)
nie jest w stanie poprawnie obsługiwać wyjątków. Nie wiesz jaki jest wyjątek; nie możesz uzyskać informacji o wyjątku. Nie masz absolutnie żadnych informacji poza faktem, że wyjątek został zgłoszony przez coś w pewnym bloku kodu. Jedyną uzasadnioną rzeczą, jaką możesz zrobić w takim bloku, jest wyczyszczenie. A to oznacza ponowne zgłoszenie wyjątku na koniec czyszczenia.To, co RAII oferuje w odniesieniu do obsługi wyjątków, to bezpłatne czyszczenie. Jeśli wszystko jest poprawnie zamknięte w RAII, wszystko zostanie odpowiednio wyczyszczone. Nie musisz już mieć
catch
instrukcji do czyszczenia. W takim przypadku nie ma powodu, aby pisaćcatch(...)
oświadczenie.Zgadzam się więc, że
catch(...)
to głównie zło ... tymczasowo .Przepis ten dotyczy właściwego wykorzystania RAII. Ponieważ bez niego musisz być w stanie wykonać pewne porządki. Nie można tego obejść; musisz być w stanie wykonać prace porządkowe. Musisz mieć pewność, że zgłoszenie wyjątku pozostawi kod w rozsądnym stanie. I
catch(...)
jest do tego niezbędnym narzędziem.Nie możesz mieć jednego bez drugiego. Nie można powiedzieć, że zarówno RAII, jak i
catch(...)
są złe. Potrzebujesz co najmniej jednego z nich; w przeciwnym razie nie jesteś wyjątkowo bezpieczny.Oczywiście istnieje jedno ważne, choć rzadkie zastosowanie,
catch(...)
którego nawet RAII nie może odrzucić:exception_ptr
skierowanie do kogoś innego. Zazwyczaj za pośrednictwempromise/future
podobnego interfejsu.Twój współpracownik jest idiotą (lub po prostu strasznie ignorantem). Powinno to być natychmiast oczywiste ze względu na to, ile kodu kopiuj i wklej sugeruje, abyś napisał. Czyszczenie każdej z tych instrukcji catch będzie dokładnie takie samo . To koszmar konserwacji, nie wspominając o czytelności.
W skrócie: jest to problem, który RAII stworzono do rozwiązania (nie dlatego, że nie rozwiązuje on innych problemów).
To, co mnie dezorientuje w tym pojęciu, to fakt, że zasadniczo jest to odwrotne do tego, jak większość ludzi twierdzi, że RAII jest zły. Ogólnie argumentem jest, że „RAII jest zły, ponieważ musisz użyć wyjątków, aby zasygnalizować awarię konstruktora. Ale nie możesz rzucać wyjątków, ponieważ nie jest to bezpieczne i będziesz musiał mieć wiele
catch
instrukcji, aby wszystko wyczyścić”. Co jest zepsutym argumentem, ponieważ RAII rozwiązuje problem, który powoduje brak RAII.Jest bardziej niż prawdopodobne, że jest przeciwko RAII, ponieważ ukrywa szczegóły. Wywołania niszczycieli nie są natychmiast widoczne w zmiennych automatycznych. Otrzymujesz kod, który jest wywoływany niejawnie. Niektórzy programiści naprawdę tego nie znoszą. Najwyraźniej do tego stopnia, że uważają, że posiadanie 3
catch
instrukcji, z których wszystkie robią to samo z kodem kopiuj i wklej, jest lepszym pomysłem.źródło
Naprawdę dwa komentarze. Po pierwsze, podczas gdy w idealnym świecie zawsze powinieneś wiedzieć, jakie wyjątki mogą zostać rzucone, w praktyce, jeśli masz do czynienia z bibliotekami stron trzecich lub kompilujesz za pomocą kompilatora Microsoft, to nie. Bardziej jednak do rzeczy; nawet jeśli znasz dokładnie wszystkie możliwe wyjątki, czy ma to znaczenie tutaj?
catch (...)
wyraża zamiar znacznie lepiej niżcatch ( std::exception const& )
, nawet zakładając, że wszystkie możliwe wyjątki pochodząstd::exception
(co miałoby miejsce w idealnym świecie). Jeśli chodzi o stosowanie kilku bloków catch, jeśli nie ma wspólnej podstawy dla wszystkich wyjątków: jest to wręcz zaciemnianie i koszmar konserwacji. Jak rozpoznać, że wszystkie zachowania są identyczne? I o to właśnie chodziło? A co się stanie, jeśli będziesz musiał zmienić zachowanie (na przykład naprawić błąd)? Zbyt łatwo jest przegapić jedną.źródło
std::exception
i codziennie próbuje wymusić jej użycie w naszej bazie kodów. Domyślam się, że próbuje mnie ukarać za używanie kodu i bibliotek zewnętrznych, których sam nie napisał.std::vector<>
może zrzucić dowolny wyjątek z dowolnego powodu.Myślę, że twój współpracownik wymieszał kilka dobrych rad - powinieneś radzić sobie ze znanymi wyjątkami w
catch
bloku, kiedy ich nie wyrzucasz.To znaczy:
Jest źle, ponieważ po cichu ukryje każdy błąd.
Jednak:
Jest w porządku - wiemy, z czym mamy do czynienia i nie musimy narażać go na kod wywołujący.
Również:
Jest w porządku, nawet najlepsza praktyka, kod zajmujący się ogólnymi błędami powinien zawierać kod, który je powoduje. Lepiej niż polegać na odbiorcy, aby wiedzieć, że transakcja wymaga wycofania się lub cokolwiek innego.
źródło
Każdej odpowiedzi twierdzącej lub nie powinno towarzyszyć uzasadnienie, dlaczego tak jest.
Mówienie, że jest to błędne tylko dlatego, że tak mnie nauczono, jest po prostu ślepym fanatyzmem.
Pisanie tego samego
//Some cleanup; throw
kilka razy, tak jak w twoim przykładzie, jest błędne, ponieważ jest to powielanie kodu i jest to obciążenie związane z utrzymaniem. Lepsze jest napisanie tego tylko raz.Napisanie w
catch(...)
celu wyciszenia wszystkich wyjątków jest złe, ponieważ powinieneś obsługiwać tylko wyjątki, które wiesz, jak sobie z tym poradzić, a dzięki temu symbolowi wieloznacznemu możesz katować więcej, niż się spodziewasz, a to może wyciszyć ważne błędy.Ale jeśli ponownie wrzucisz po a
catch(...)
, to drugie uzasadnienie nie ma już zastosowania, ponieważ tak naprawdę nie zajmujesz się wyjątkiem, więc nie ma powodu, dla którego powinno się to zniechęcać.W rzeczywistości zrobiłem to w celu zalogowania wrażliwych funkcji bez żadnego problemu:
źródło
Log(...)
nie można rzucić.Generalnie zgadzam się z nastrojem zamieszczonych tutaj postów, naprawdę nie podoba mi się wzór wychwytywania określonych wyjątków - myślę, że składnia tego jest wciąż w powijakach i nie jest jeszcze w stanie poradzić sobie z niepotrzebnym kodem.
Ale ponieważ wszyscy to mówią, przyjmuję do wiadomości fakt, że chociaż używam ich oszczędnie, często patrzyłem na jedno z moich stwierdzeń „catch (Exception e)” i powiedziałem „Cholera, chciałbym zadzwonić poza tym szczególnym wyjątkiem ”, ponieważ kiedy przychodzisz później, często miło jest wiedzieć, jaki był zamiar i co klient rzuci na pierwszy rzut oka.
Nie usprawiedliwiam postawy „Zawsze używaj x”, po prostu mówię, że czasami miło jest widzieć je na liście i jestem pewien, że niektórzy ludzie uważają, że jest to właściwa droga.
źródło