C++

Obsługa wyjątków w C++

Obsługa wyjątków w C++
Istnieją trzy rodzaje błędów oprogramowania. Są to błędy składni, błędy logiczne i błędy wykonawcze.

Błędy składni

Źle wpisane wyrażenie, instrukcja lub konstrukcja jest błędem składni.

Rozważ następujące dwa stwierdzenia:

int przyw[] = 1, 2, 3; //poprawny
int arr = 1, 2, 3; //błąd składni, brak []

Są to definicje tej samej tablicy. Pierwszy jest poprawny. Drugiego brakuje [], a to jest błąd składni. Nie udało się skompilować programu z błędem składni. Kompilacja kończy się niepowodzeniem z komunikatem o błędzie wskazującym na błąd składni. Dobrą rzeczą jest to, że błąd składni można zawsze naprawić, jeśli programista wie, co robi.

Błąd logiczny

Błąd logiczny to błąd popełniony przez programistę w przypadku nieprawidłowego kodowania logicznego. Może to być wynikiem niewiedzy programisty na temat funkcji języka programowania lub niezrozumienia tego, co program powinien zrobić.

W tej sytuacji program kompiluje się pomyślnie. Program działa dobrze, ale daje złe wyniki. Taki błąd może być spowodowany wykonaniem pętli iteracyjnej 5 razy, podczas gdy wykonuje się iterację 10 razy. Może się również zdarzyć, że pętla jest nieświadomie tworzona, aby iterować w nieskończoność. Jedynym sposobem na rozwiązanie tego rodzaju błędu jest dokładne zaprogramowanie i dokładne przetestowanie programu przed przekazaniem go klientowi.

Błędy uruchomieniowe

Błędne lub wyjątkowe dane wejściowe powodują błędy w czasie wykonywania run. W tym przypadku program został skompilowany pomyślnie i sprawdza się w wielu sytuacjach. W pewnych sytuacjach program ulega awarii (i zatrzymuje się).

Wyobraź sobie, że w segmencie kodu programu 8 należy podzielić przez liczbę mianowników. Jeśli więc licznik 8 podzielimy przez mianownik 4, odpowiedź (iloraz) będzie równa 2. Jeśli jednak użytkownik wprowadzi 0 jako mianownik, program ulegnie awarii. Dzielenie przez 0 nie jest dozwolone w matematyce, a także nie jest dozwolone w obliczeniach. W programowaniu należy unikać dzielenia przez zero. Obsługa wyjątków obsługuje błędy uruchomieniowe, takie jak dzielenie przez zero. Poniższy program pokazuje, jak poradzić sobie z problemem dzielenia przez zero bez używania funkcji wyjątku w C++:

#zawierać
przy użyciu standardowej przestrzeni nazw;
int main()

int licznik = 8;
int mianownik = 2;
jeśli (mianownik != 0 )

int wynik = licznik/mianownik;
Cout << result << '\n';

jeszcze

Cout << "Division by zero is not permitted!" << '\n';

zwróć 0;

Wyjście to 4. Gdyby mianownik wynosił 0, wynik byłby następujący:

„Dzielenie przez zero jest niedozwolone”!”

Głównym kodem jest tutaj konstrukcja if-else. Jeśli mianownik nie jest równy 0, nastąpi podział; jeśli wynosi 0, podział nie nastąpi will. Do użytkownika zostanie wysłany komunikat o błędzie, a program będzie nadal działał bez awarii. Błędy uruchomieniowe są zwykle obsługiwane przez unikanie wykonania segmentu kodu i wysyłanie komunikatu o błędzie do użytkownika.

Funkcja wyjątków w C++ używa bloku try dla bloku if i bloku catch dla bloku else do obsługi błędu, tak jak poniżej:

#zawierać
przy użyciu standardowej przestrzeni nazw;
int main()

int licznik = 8;
int mianownik = 2;
próbować

jeśli (mianownik != 0 )

int wynik = licznik/mianownik;
Cout << result << '\n';

jeszcze

rzut 0;


złapać (wewnętrzny błąd)

jeśli (błąd == 0)
Cout << "Division by zero is not permitted!" << '\n';

zwróć 0;

Zauważ, że nagłówek try nie zawiera argumentu. Zauważ też, że catch-block, który jest jak definicja funkcji, ma parametr. Typ parametru musi być taki sam jak operand (argument) wyrażenia-rzutu. Wyrażenie rzutu znajduje się w bloku try. Zrzuca argument wyboru programisty, który jest związany z błędem, a catch-block go przechwytuje. W ten sposób kod w bloku try nie jest wykonywany. Następnie blok catch wyświetla komunikat o błędzie.

Ten artykuł wyjaśnia obsługę wyjątków w C++. Podstawowa znajomość C++ jest warunkiem wstępnym zrozumienia tego artykułu przez czytelnika.

Treść artykułu:

  • Funkcja zgłaszająca wyjątek
  • Więcej niż jeden blok na jeden blok próbny
  • Zagnieżdżone bloki try/catch
  • noexcept-specyfikator
  • Specjalna funkcja std::terminate()
  • Wniosek

Funkcja zgłaszająca wyjątek:

Funkcja może również zgłosić wyjątek, tak jak robi to blok try. Rzucanie odbywa się w ramach definicji funkcji. Poniższy program ilustruje to:

#zawierać
przy użyciu standardowej przestrzeni nazw;
void fn(const char* str)

if (islower(str[0]))
rzuć „l”;

int main()

próbować

fn("kowalski");

połów (char ch)

jeśli (ch == 'l')
Cout << "Person's name cannot begin in lowercase!" << '\n';

zwróć 0;

Zauważ, że tym razem blok try ma tylko wywołanie funkcji. Jest to wywoływana funkcja, która ma operację rzutu. Blok catch przechwytuje wyjątek, a wyjściem jest:

„Imię i nazwisko osoby nie może zaczynać się małymi literami!”

Tym razem typem rzuconym i złapanym jest char.

Więcej niż jeden blok przechwytujący na jeden blok próbny:

Na jeden try-blok może przypadać więcej niż jeden blok catch. Wyobraź sobie sytuację, w której dane wejściowe mogą być dowolnymi znakami klawiatury, ale nie cyfrą ani alfabetem. W tym przypadku muszą istnieć dwa bloki catch: jeden dla liczby całkowitej do sprawdzania cyfry i drugi dla znaku do sprawdzania alfabetu. Poniższy kod ilustruje to:

#zawierać
przy użyciu standardowej przestrzeni nazw;
wejście znaków = '*';
int main()

próbować

if (isdigit(wejście))
rzut 10;
jeśli (isalfa(wejście))
rzuć „z”;

złapać (wew.)

Cout << "Digit input is forbidden!" << '\n';

złapać (znak)

Cout << "Character input is forbidden!" << '\n';

zwróć 0;

Nie ma wyjścia. Jeśli wartość wejścia była cyfrą, e.sol., „1”, wynik byłby następujący:

„Wprowadzanie cyfr jest zabronione!"

Gdyby wartość wejścia była alfabetem, e.sol., „a”, wynik byłby następujący:

„Wprowadzanie znaków jest zabronione!"

Zauważ, że na liście parametrów dwóch bloków catch nie ma nazwy identyfikatora. Należy również zauważyć, że w definicji dwóch bloków catch, poszczególne rzucone argumenty nie zostały zweryfikowane, czy ich wartości są dokładne, czy nie.

Dla połowu liczy się typ; chwyt musi pasować do typu rzuconego operandu. Konkretna wartość rzuconego argumentu (operandu) może być w razie potrzeby użyta do dalszej weryfikacji.

Więcej niż jeden uchwyt dla tego samego typu

Możliwe jest posiadanie dwóch handlerów tego samego typu. Gdy zostanie zgłoszony wyjątek, kontrola jest przekazywana do najbliższego programu obsługi z pasującym typem. Poniższy program ilustruje to:

#zawierać
przy użyciu standardowej przestrzeni nazw;
wejście znakowe = '1';
int main()

próbować

if (isdigit(wejście))
rzut 10;

złapać (wew.)

Cout << "Digit input is forbidden!" << '\n';

złapać (wew.)

Cout << "Not allowed at all: digit input!" << '\n';

zwróć 0;

Dane wyjściowe to:

„Wprowadzanie cyfr jest zabronione!"

Zagnieżdżone bloki try/catch:

bloki try/catch mogą być zagnieżdżane. Powyższy program do wprowadzania znaków niealfanumerycznych z klawiatury jest tutaj powtórzony, ale z zagnieżdżonym alfabetycznym kodem błędu:

#zawierać
przy użyciu standardowej przestrzeni nazw;
wejście znaków = '*';
int main()

próbować

if (isdigit(wejście))
rzut 10;
próbować

jeśli (isalfa(wejście))
rzuć „z”;

złapać (znak)

Cout << "Character input is forbidden!" << '\n';


złapać (wew.)

Cout << "Digit input is forbidden!" << '\n';

zwróć 0;

Alfabetyczny blok try/catch-block błędu jest zagnieżdżony w bloku try kodu cyfrowego. Działanie tego programu i poprzednia operacja, z której został skopiowany, są takie same.

noexcept-specyfikator

Rozważ następującą funkcję:

void fn(const char* str) noexcept

if (islower(str[0]))
rzuć „l”;

Zwróć uwagę na specyfikator 'noexcept' zaraz po prawym nawiasie listy parametrów funkcji function. Oznacza to, że funkcja nie powinna zgłaszać wyjątku. Jeśli funkcja zgłosi wyjątek, jak w tym przypadku, skompiluje się z komunikatem ostrzegawczym, ale nie uruchomi się. Próba uruchomienia programu spowoduje wywołanie funkcji specjalnej std::terminate(), która powinna wdzięcznie zatrzymać program, zamiast pozwolić mu dosłownie się zawiesić.

Specyfikator noexcept występuje w różnych formach. Są to:

wpisz func() noexcept; : nie pozwala na wyrażenie rzutu
wpisz func() noexcept(prawda); : pozwala na wyrażenie rzutu
wpisz func() throw(); : nie pozwala na wyrażenie rzutu
wpisz func() noexcept(false); : pozwala na wyrażenie rzutu, które jest opcjonalne
wpisz func(); : pozwala na wyrażenie rzutu, które jest opcjonalne

prawda lub fałsz w nawiasach można zastąpić wyrażeniem, którego wynikiem jest prawda lub fałsz.

Specjalna funkcja std::terminate():

Jeśli nie można obsłużyć wyjątku, należy go zgłosić ponownie. W tym przypadku rzucone wyrażenie może, ale nie musi mieć operandu. Funkcja specjalna std::terminate() zostanie wywołana w czasie wykonywania, co powinno wdzięcznie zatrzymać program, a nie tylko pozwolić mu się dosłownie zawiesić.

Wpisz, skompiluj i uruchom następujący program:

#zawierać
przy użyciu standardowej przestrzeni nazw;
wejście znakowe = '1';
int main()

próbować

if (isdigit(wejście))
rzut 10;

złapać (wew.)

rzucać;

zwróć 0;

Po udanej kompilacji program zakończył działanie bez uruchamiania, a komunikat o błędzie z komputera autora to:

„zakończyć wywołane po rzuceniu wystąpienia „int”

Przerwano (rdzeń zrzucony)”

Wniosek:

Funkcja wyjątków w C++ uniemożliwia wykonanie segmentu kodu w oparciu o jakiś rodzaj danych wejściowych. Program kontynuuje działanie w razie potrzeby. Konstrukcja wyjątku (zapobiegania błędom) składa się z bloku try i bloku catch. Blok try ma interesujący segment kodu, który może zostać pominięty, w zależności od pewnych warunków wejściowych. Try-block ma wyrażenie throw, które rzuca operand. Ten operand jest również nazywany wyjątkiem. Jeśli typ operandu i typ parametru bloku catch są takie same, wyjątek jest przechwytywany (obsługiwany). Jeśli wyjątek nie zostanie przechwycony, program zostanie zakończony, ale nadal bądź bezpieczny, ponieważ segment kodu, który miał zostać wykonany, aby dać zły wynik, nie został wykonany. Typowa obsługa wyjątków polega na pominięciu segmentu kodu i wysłaniu do użytkownika komunikatu o błędzie. Segment kodu jest wykonywany dla normalnego wejścia, ale pomijany dla błędnych wejść.

Gry Open Source Ports of Commercial Game Engines
Open Source Ports of Commercial Game Engines
Free, open source and cross-platform game engine recreations can be used to play old as well as some of the fairly recent game titles. This article wi...
Gry Najlepsze gry wiersza poleceń dla systemu Linux
Najlepsze gry wiersza poleceń dla systemu Linux
Wiersz poleceń jest nie tylko twoim największym sprzymierzeńcem podczas korzystania z Linuksa - może być również źródłem rozrywki, ponieważ możesz go ...
Gry Najlepsze aplikacje do mapowania gamepada dla systemu Linux
Najlepsze aplikacje do mapowania gamepada dla systemu Linux
Jeśli lubisz grać w gry na Linuksie za pomocą gamepada zamiast typowego systemu wprowadzania klawiatury i myszy, jest kilka przydatnych aplikacji dla ...