Jak: Projektowanie dla bezpieczeństwa wyjątek (C++)
Jedną z zalet mechanizmu wyjątek jest że wykonanie, wraz z danymi o wyjątku, przechodzi bezpośrednio z instrukcji, która zgłasza wyjątek do pierwszej instrukcji, jaki obsługuje on catch.Program obsługi może być dowolną liczbę poziomów w górę w stos wywołań.Funkcje, które są wywoływane między instrukcji try i instrukcji throw nie muszą mieć żadnych informacji o wyjątku, który jest generowany.Jednakże muszą być tak zaprojektowane, aby szybko wychodzą z zakresu "nieoczekiwane" w dowolnym punkcie, gdzie może rozprzestrzeniać się z dołu wyjątek, a więc nie pozostawiając za częściowo tworzonych obiektów, przecieku pamięci lub struktur danych, które są w Państwach niezdatny do użytku.
Podstawowe techniki
Solidne zasad obsługi wyjątków wymaga refleksji i powinny być częścią procesu projektowania.Ogólnie rzecz biorąc większości wyjątków są wykrywane i wyrzucane na niższych warstw moduł oprogramowania, ale zazwyczaj te warstwy nie ma wystarczającej ilości kontekst do obsługi błędu, lub narażać wiadomości do użytkowników końcowych.W środku warstw funkcje można złapać i ponownego zgłoszenia wyjątek, gdy trzeba sprawdzić obiekt wyjątku lub mają dodatkowe przydatne informacje zapewniające górną warstwę, która ostatecznie przechwytuje wyjątek.Funkcja powinna złapać i "połknąć" wyjątek tylko wtedy, gdy jest w stanie całkowicie odzyskać z niego.W wielu przypadkach poprawne zachowanie w środku warstw jest umożliwienie wyjątek propagować się stos wywołań.Nawet w najwyższej warstwie właściwe byłoby umożliwienie nieobsłużony wyjątek zakończyć program, jeśli wyjątek kończy pracę z programem w Państwie, w którym nie może zagwarantować jej poprawności.
Bez względu na to, jak funkcja obsługuje wyjątek, aby pomóc w zagwarantowaniu, że jest "bezpieczny wyjątek," muszą być zaprojektowane zgodnie z zasadami następujące.
Zachować proste klas zasobu
Podczas hermetyzacji jest zarządzanie zasobami ręczne w klasach, należy użyć klasy, która nie robi nic innego do zarządzania każdego zasobu; w przeciwnym razie można wprowadzić przecieków.
Zarządzanie zasobami za pomocą języka RAII
Być odławiany wyjątek, funkcję muszą zapewnić, że obiekty, że została przydzielona przy użyciu malloc lub new zostały zniszczone, a wszystkie zasoby, takie jak dojścia do plików zostały zamknięte lub zwolniony nawet wtedy, gdy występuje wyjątek.Inicjalizacji jest przejęcie zasobu języka (RAII) więzi zarządzania takich zasobów, do czasu działania zmienne automatyczne.Gdy funkcja wykracza poza zakres, przywracając normalnie lub z powodu wyjątku, są wywoływane destruktory dla wszystkich skonstruowanym automatyczne zmiennych.Obiekt otoki RAII takie jak połączenia inteligentnego wskaźnika właściwego usuwania lub zamykania funkcji w jego destruktor.W wyjątek-kod zarządzany jest niezwykle ważne przekazać własność każdego zasobu natychmiast do pewnego rodzaju obiektu RAII.Należy zauważyć, że vector, string, make_shared, fstream, i podobne klas obsługi uzyskiwanie zasobu. Jednakże unique_ptr i tradycyjnych shared_ptr konstrukcji są specyficzne, ponieważ pozyskiwanie zasobów jest wykonywane przez użytkownika, a nie obiektu; w związku z tym, liczą się jako Zasobów wydania jest zniszczenie , ale są wątpliwe jako RAII.
Trzy gwarancje wyjątek
Zazwyczaj bezpieczeństwa wyjątek jest omówione w kontekście gwarancje trzech wyjątków, które udostępniają funkcję: nie fail gwarancji, gwarantemi podstawowe gwarancji.
Nr fail gwarancji
Gwarancja nie fail (lub "nie-throw") jest najsilniejszą gwarancji, że funkcja może dostarczyć.Stwierdza się, że funkcja nie Zgłoś wyjątek, lub pozwalają na propagowanie.Niezawodnie nie może jednak dostarczyć takiej gwarancji, chyba że () wiedzieć, że wszystkie funkcje, które wywołuje tę funkcję są również nie fail, (b) program wiedzieć, że wszelkie wyjątki, które są wyrzucane zostały złowione, zanim dotrą do tej funkcji lub (c) wiesz, jak do połowu i poprawnie przetwarza wszystkie wyjątki, które mogą zostać dostarczone do tej funkcji.
Silnej gwarancji i podstawowe gwarancji opierają się na założeniu, że destruktory są nr nie.Wszystkie pojemniki i typy w bibliotece standardowej zagwarantować ich destruktory nie rzucać.Istnieje również wymóg rozmawiać: C++ Reference wymaga, że zdefiniowany przez użytkownika typy, które podane są do niego — na przykład jako argumenty szablonu — musi mieć-wyrzucanie destruktorów.
Gwarantem
Silna gwarancja stwierdza, że jeśli funkcja wykracza poza zakres z powodu wyjątku, nie nastąpi wyciek pamięci i program stanu nie będzie modyfikowana.Funkcja, która zapewnia gwarantem jest zasadniczo transakcji, która ma semantykę commit lub rollback: albo całkowicie skutku lub nie ma wpływu.
Podstawowe gwarancji
Podstawowe gwarancji jest najsłabszy z trzech.Jednak gdy gwarantem jest zbyt drogie, zużycie pamięci lub w przebiegu może być najlepszym wyborem.Podstawowe zagwarantować Państwa, że jeśli wystąpi wyjątek, przeciekają Brak pamięci i obiekt jest wciąż w stanie, mimo że danych została zmodyfikowana.
Bezpieczny wyjątek klas
Klasa może pomóc zapewnić ich własne bezpieczeństwo wyjątek, nawet wtedy, gdy jest ona wykorzystywana przez niebezpieczne funkcje poprzez zapobieganie się częściowo zbudowanych lub częściowo zniszczone.Jeśli Konstruktor klasy kończy pracę przed zakończeniem, nigdy nie jest tworzony obiekt i jego destruktora nigdy nie zostanie wywołana.Mimo że automatyczne zmiennych, które są inicjowane przed wyjątek będzie miał swoje destruktory wywoływana dynamicznie przydzielonego pamięci lub zasobów, które nie są zarządzane przez inteligentnego wskaźnika lub podobne automatyczne zmienna będzie wycieku.
Wbudowane typy są wszystkie nie fail i typów standardowa biblioteka obsługuje podstawowe gwarancji co najmniej.Postępuj zgodnie ze wskazówkami dla dowolnego typu danych zdefiniowanych przez użytkownika, które musi być bezpieczny wyjątek:
Do zarządzania wszystkie zasoby, należy użyć Sprytne wskaźniki lub innych opakowań typu RAII.Funkcje zarządzania zasobami w destruktor klasy należy unikać, ponieważ destruktor nie zostanie wywołany, jeśli Konstruktor zgłasza wyjątek.Jednakże jeśli klasa jest dedykowany WSRM, który kontroluje tylko jeden zasób, to można zastosować destruktor do zarządzania zasobami.
Rozumiem, że wyjątek w konstruktorze klasy bazowej nie może zostać połknięte w konstruktorze klasy pochodnej.Jeśli chcesz tłumaczyć i ponowne zgłoszenie wyjątku klasy podstawowej w Konstruktorze pochodnych, należy użyć bloku try funkcji.Aby uzyskać więcej informacji, zobacz Jak: obsługi wyjątków w Konstruktory klasy bazowej (C++).
Należy rozważyć, czy ma być przechowywany w element członkowski danych, który jest zawijany w inteligentny wskaźnik, stan całej klasy, zwłaszcza, jeśli klasa ma pojęcie "inicjowania, która może zakończyć się niepowodzeniem." Mimo że C++ pozwala na niezainicjowane zmienne składowe, nie obsługuje wystąpień klas niezainicjowany lub częściowo zainicjować.Konstruktor musi kończy się pomyślnie lub zakończyć się niepowodzeniem; żaden obiekt nie jest tworzony, jeżeli nie można uruchomić Konstruktora do zakończenia.
Nie dopuszczać żadnych wyjątków wyjść z destruktora.Podstawowe aksjomat C++ jest, że destruktory nigdy nie powinny umożliwić wyjątek do propagowania górę stosu wywołań.Jeśli destruktora musi wykonać operację potencjalnie wyrzucanie wyjątków, musi więc w bloku try Blok catch i połknąć wyjątku.Standardowa biblioteka zawiera Niniejsza gwarancja wszystkich destruktory, którą definiuje.
Zobacz też
Koncepcje
Błędy i obsługi wyjątków (Podręcznik programowania C++ nowoczesny)