Projektując moją pierwszą „poważną” bibliotekę C ++, zadaję sobie pytanie:
Czy to dobry styl czerpać wyjątki std::exception
i to jest potomstwo ?!
Nawet po przeczytaniu
Wciąż nie jestem pewien. Ponieważ, poza powszechną (ale może nie dobrą) praktyką, jako użytkownik biblioteki zakładam, że funkcja biblioteczna rzucałaby std::exception
tylko wtedy, gdy standardowe funkcje biblioteczne zawiodły w implementacji biblioteki, i nic na to nie poradzi. Ale mimo to, pisząc kod aplikacji, jest to dla mnie bardzo wygodne, a także IMHO dobrze wygląda po prostu rzucić std::runtime_error
. Również moi użytkownicy mogą polegać na zdefiniowanym minimalnym interfejsie, takim jak what()
lub kody.
I na przykład mój użytkownik podaje błędne argumenty, co byłoby wygodniejsze, niż rzucić std::invalid_argument
, prawda? Tak więc w połączeniu z powszechnym użyciem std :: wyjątku widzę w innym kodzie: Dlaczego nie pójść jeszcze dalej i wywodzić się z niestandardowej klasy wyjątków (np. Lib_foo_exception), a także z std::exception
.
Myśli?
źródło
std::exception
nie znaczy rzucaćstd::exception
. Ponadtostd::runtime_error
dziedziczystd::exception
on po pierwsze, awhat()
metoda pochodzistd::exception
, a niestd::runtime_error
. I zdecydowanie powinieneś stworzyć własne klasy wyjątków zamiast generować wyjątki ogólne, takie jakstd::runtime_error
.lib_foo_exception
klasa wywodzi sięstd::exception
, użytkownik biblioteki łapałbylib_foo_exception
po prostu łapiącstd::exception
, oprócz tego, kiedy łapał tylko bibliotekę. Więc mógłbym również zapytać, czy klasa główna wyjątków mojej biblioteki dziedziczy po std :: wyjątek .lib_foo_exception
?” Dzięki dziedziczeniustd::exception
możesz to zrobić przezcatch(std::exception)
LUBcatch(lib_foo_exception)
. Bez wywodzeniastd::exception
złapałbyś go wtedy i tylko wtedy , gdycatch(lib_foo_exception)
.catch(...)
. Jest tak, ponieważ język uwzględnia przypadek, który rozważasz (i biblioteki „źle zachowujące się”), ale to nie jest najlepsza współczesna praktyka.catch
witryn, a także bardziej zgrubnych transakcji, które modelują operacje użytkownika końcowego. Jeśli porównasz to z językami, które nie promują idei uogólnionego łapaniastd::exception&
, np. Często mają o wiele więcej kodu ztry/catch
blokami pośredniczącymi dotyczącymi bardzo specyficznych błędów, co nieco zmniejsza ogólną obsługę wyjątków, gdy zaczyna się umieszczać znacznie większy nacisk na ręczne zarządzanie błędami, a także na wszystkie różne błędy, które mogą wystąpić.Odpowiedzi:
Wszystkie wyjątki powinny dziedziczyć
std::exception
.Załóżmy na przykład, że muszę zadzwonić
ComplexOperationThatCouldFailABunchOfWays()
i chcę poradzić sobie z wyjątkami, które może wywołać . Jeśli wszystko dziedziczystd::exception
, jest to łatwe. Potrzebuję tylko jednegocatch
bloku i mam standardowy interfejs (what()
) do uzyskiwania szczegółów.Jeśli wyjątki NIE dziedziczą
std::exception
, staje się to znacznie brzydsze:Odnośnie do rzucania
runtime_error
lubinvalid_argument
tworzenia własnychstd::exception
podklas do rzucania: Moją ogólną zasadą jest wprowadzanie nowej podklasy za każdym razem, gdy potrzebuję obsługiwać określony typ błędu inaczej niż inne błędy (tj. Za każdym razem, gdy potrzebuję osobnegocatch
bloku).runtime_error
wyrzucony tutaj oznacza coś innego niż ogólny błąd czasu wykonywania), wówczas ryzykuję konflikt z innymi zastosowaniami istniejącej podklasy.invalid_argument
), To ponownie używam istniejącej klasy. Po prostu nie widzę większych korzyści z dodania nowej klasy w tym przypadku. (Podstawowe wytyczne C ++ nie zgadzają się ze mną tutaj - zalecają zawsze używanie własnych klas).W ++ Wytyczne C rdzeniowe mają dalej dyskusja i przykłady.
źródło
catch (...)
(z dosłowną elipsą)?catch (...)
przydaje się tylko wtedy, gdy nie musisz nic robić z tym, co zostało rzucone. Jeśli chcesz coś zrobić - np. Wyświetlić lub zapisać konkretny komunikat o błędzie, jak w moim przykładzie - musisz wiedzieć, co to jest.To błędne założenie.
Standardowe typy wyjątków są przewidziane dla „powszechniejszych” zastosowań. Nie są przeznaczone wyłącznie do tego używania przez bibliotekę standardową.
Tak, spraw, aby wszystko ostatecznie odziedziczyło
std::exception
. Często wymaga to dziedziczenia zstd::runtime_error
lubstd::logic_error
. Cokolwiek jest odpowiednie dla wdrażanej klasy wyjątków.Jest to oczywiście subiektywne - kilka popularnych bibliotek całkowicie ignoruje standardowe typy wyjątków, prawdopodobnie w celu oddzielenia bibliotek od standardowej biblioteki. Osobiście uważam, że to wyjątkowo samolubne! Sprawia, że łapanie wyjątków jest o wiele trudniejsze.
Mówiąc osobiście, często po prostu rzucam
std::runtime_error
i skończę z tym. Ale to prowadzi do dyskusji na temat tego, jak szczegółowe jest tworzenie klas wyjątków, o co nie pytasz.źródło