I C++ är undantag körtidsavvikelser eller onormala förhållanden som ett program stöter på under dess körning. Processen att hantera dessa undantag kallas undantagshantering. Med hjälp av undantagshanteringsmekanismen kan kontrollen från en del av programmet där undantaget inträffade överföras till en annan del av koden.
Så genom att i princip använda undantagshantering i C++ kan vi hantera undantagen så att vårt program fortsätter att köras.
rutnätslayout
Vad är ett C++-undantag?
Ett undantag är ett oväntat problem som uppstår under körningen av ett program. Vårt program avslutas plötsligt med några fel/problem. Undantag inträffar under körning av programmet (runtime).
Typer av C++ undantag
Det finns två typer av undantag i C++
- Synkron: Undantag som händer när något går fel på grund av ett misstag i indata eller när programmet inte är utrustat för att hantera den aktuella typen av data som det arbetar med, som att dividera ett tal med noll.
- Asynkron : Undantag som ligger utanför programmets kontroll, som skivfel, tangentbordsavbrott, etc.
C++ försök och fånga
C++ tillhandahåller en inbyggd funktion för undantagshantering. Det kan göras med hjälp av följande specialiserade nyckelord: prova, fånga och kasta med vart och ett med olika syfte.
Syntax för try-catch i C++
try { // Code that might throw an exception throw SomeExceptionType('Error message'); } catch ( ExceptionName e1 ) { // catch block catches the exception that is thrown from try block }> 1. försök i C++
Nyckelordet try representerar ett kodblock som kan skapa ett undantag placerat i try-blocket. Den följs av ett eller flera fångstblock. Om ett undantag inträffar, försök att blockera det undantaget.
2. fånga i C++
Catch-satsen representerar ett kodblock som exekveras när ett visst undantag kastas från try-blocket. Koden för att hantera undantaget skrivs inuti catch-blocket.
3. kasta in C++
Ett undantag i C++ kan skapas med nyckelordet throw. När ett program stöter på en throw-sats, avslutar det omedelbart den aktuella funktionen och börjar hitta ett matchande catch-block för att hantera det kastade undantaget.
Notera: Flera catch-satser kan användas för att fånga olika typer av undantag som kastas av try-block.
Nyckelorden try and catch kommer i par: Vi använder try-blocket för att testa en del kod och om koden ger ett undantag kommer vi att hantera det i vårt catch-block.
Varför behöver vi Undantagshantering i C++?
Följande är de viktigaste fördelarna med undantagshantering jämfört med traditionell felhantering:
- Separation av felhanteringskod från normal kod : Det finns alltid if-else-villkor för att hantera fel i traditionella felhanteringskoder. Dessa villkor och koden för att hantera fel blandas ihop med det normala flödet. Detta gör koden mindre läsbar och underhållbar. Med try/catch-block blir koden för felhantering skild från det normala flödet.
- Funktioner/metoder kan endast hantera de undantag de väljer : En funktion kan skapa många undantag, men kan välja att hantera några av dem. Övriga undantag, som kastas men inte fångas upp, kan den som ringer hanteras. Om den som ringer väljer att inte fånga dem, hanteras undantagen av den som ringer.
I C++ kan en funktion ange undantagen som den kastar med hjälp av nyckelordet throw. Den som anropar denna funktion måste hantera undantaget på något sätt (antingen genom att specificera det igen eller fånga upp det).
- Gruppering av feltyper : I C++ kan både grundläggande typer och objekt kastas som undantag. Vi kan skapa en hierarki av undantagsobjekt, gruppera undantag i namnutrymmen eller klasser och kategorisera dem efter deras typ.
Exempel på undantagshantering i C++
Följande exempel visar hur man använder ett try-catch-block för att hantera undantag i C++.
Exempel 1
Exemplet nedan visar undantag för kast i C++.
C++
// C++ program to demonstate the use of try,catch and throw> // in exception handling.> #include> #include> using> namespace> std;> int> main()> {> >// try block> >try> {> >int> numerator = 10;> >int> denominator = 0;> >int> res;> >// check if denominator is 0 then throw runtime> >// error.> >if> (denominator == 0) {> >throw> runtime_error(> >'Division by zero not allowed!'>);> >}> >// calculate result if no exception occurs> >res = numerator / denominator;> >//[printing result after division> >cout <<>'Result after division: '> << res << endl;> >}> >// catch block to catch the thrown exception> >catch> (>const> exception& e) {> >// print the exception> >cout <<>'Exception '> << e.what() << endl;> >}> >return> 0;> }> |
>
>Produktion
Exception Division by zero not allowed!>
Exempel 2
Följande är ett enkelt exempel för att visa undantagshantering i C++. Utdata från programmet förklarar flödet av exekvering av försök/fånga block.
CPP
anslutning java mysql
// C++ program to demonstate the use of try,catch and throw> // in exception handling.> #include> using> namespace> std;> int> main()> {> >int> x = -1;> >// Some code> >cout <<>'Before try
'>;> >// try block> >try> {> >cout <<>'Inside try
'>;> >if> (x <0) {> >// throwing an exception> >throw> x;> >cout <<>'After throw (Never executed)
'>;> >}> >}> >// catch block> >catch> (>int> x) {> >cout <<>'Exception Caught
'>;> >}> >cout <<>'After catch (Will be executed)
'>;> >return> 0;> }> |
>
>Produktion
Before try Inside try Exception Caught After catch (Will be executed)>
Egenskaper för undantagshantering i C++
Fastighet 1
Det finns ett speciellt fångstblock som kallas 'catch-all'-blocket, skrivet som catch(...), som kan användas för att fånga alla typer av undantag.
Exempel
I följande program kastas en int som ett undantag, men det finns inget catch-block för int, så catch(...)-blocket kommer att exekveras.
CPP
// C++ program to demonstate the use of catch all> // in exception handling.> #include> using> namespace> std;> int> main()> {> >// try block> >try> {> >// throw> >throw> 10;> >}> >// catch block> >catch> (>char>* excp) {> >cout <<>'Caught '> << excp;> >}> >// catch all> >catch> (...) {> >cout <<>'Default Exception
'>;> >}> >return> 0;> }> |
>
>Produktion
Default Exception>
Fastighet 2
Implicit typkonvertering sker inte för primitiva typer.
Exempel
I följande program konverteras inte 'a' implicit till int.
CPP
//// C++ program to demonstate property 2: Implicit type> /// conversion doesn't happen for primitive types.> // in exception handling.> #include> using> namespace> std;> int> main()> {> >try> {> >throw> 'a'>;> >}> >catch> (>int> x) {> >cout <<>'Caught '> << x;> >}> >catch> (...) {> >cout <<>'Default Exception
'>;> >}> >return> 0;> }> |
>
>Produktion
negation diskret matematik
Default Exception>
Produktion:
Default Exception>
Fastighet 3
Om ett undantag kastas och inte fångas någonstans, avslutas programmet på ett onormalt sätt.
Exempel
I följande program kastas en röding, men det finns inget fångstblock för att fånga rödingen.
CPP
// C++ program to demonstate property 3: If an exception is> // thrown and not caught anywhere, the program terminates> // abnormally in exception handling.> #include> using> namespace> std;> int> main()> {> >try> {> >throw> 'a'>;> >}> >catch> (>int> x) {> >cout <<>'Caught '>;> >}> >return> 0;> }> |
>
>
Produktion
terminate called after throwing an instance of 'char'>
Vi kan ändra detta onormala uppsägningsbeteende genom att skriva vår oväntade funktion.
Notera : Ett härlett klassundantag bör fångas före ett basklassundantag.
Liksom Java har C++-biblioteket en standardundantag klass som är basklassen för alla standardundantag. Alla objekt som kastas av komponenterna i standardbiblioteket härleds från denna klass. Därför kan alla standardundantag fångas genom att fånga denna typ.
Fastighet 4
Till skillnad från Java, i C++, är alla undantag avmarkerade, det vill säga kompilatorn kontrollerar inte om ett undantag fångas eller inte (se detta för detaljer). Så det är inte nödvändigt att ange alla ouppfångade undantag i en funktionsdeklaration. Men, undantagshantering är det en rekommenderad praxis att göra det.
Exempel
Följande program kompilerar bra, men helst bör signaturen för fun() lista de omarkerade undantagen.
CPP
// C++ program to demonstate property 4 in exception> // handling.> #include> using> namespace> std;> // This function signature is fine by the compiler, but not> // recommended. Ideally, the function should specify all> // uncaught exceptions and function signature should be> // 'void fun(int *ptr, int x) throw (int *, int)'> void> fun(>int>* ptr,>int> x)> {> >if> (ptr == NULL)> >throw> ptr;> >if> (x == 0)> >throw> x;> >/* Some functionality */> }> int> main()> {> >try> {> >fun(NULL, 0);> >}> >catch> (...) {> >cout <<>'Caught exception from fun()'>;> >}> >return> 0;> }> |
>
>Produktion
Caught exception from fun()>
Ett bättre sätt att skriva ovanstående kod:
CPP
// C++ program to demonstate property 4 in better way> #include> using> namespace> std;> // Here we specify the exceptions that this function> // throws.> void> fun(>int>* ptr,>int> x)>throw>(> >int>*,>int>)>// Dynamic Exception specification> {> >if> (ptr == NULL)> >throw> ptr;> >if> (x == 0)> >throw> x;> >/* Some functionality */> }> int> main()> {> >try> {> >fun(NULL, 0);> >}> >catch> (...) {> >cout <<>'Caught exception from fun()'>;> >}> >return> 0;> }> |
>
>
java sammanlänkande strängarProduktion
Caught exception from fun()>
Notera : Användningen av Dynamic Exception Specification har fasats ut sedan C++11. En av anledningarna till det kan vara att det slumpmässigt kan avbryta ditt program. Detta kan hända när du kastar ett undantag av en annan typ som inte nämns i den dynamiska undantagsspecifikationen. Ditt program kommer att avbryta sig själv eftersom det i det scenariot anropar (indirekt) terminate(), som som standard anropar abort().
Fastighet 5
I C++ kan try/catch-block kapslas. Ett undantag kan också kastas om med throw; .
Exempel
Följande program visar försök/fånga block som kapslar.
CPP
fcfs
// C++ program to demonstrate try/catch blocks can be nested> // in C++> #include> using> namespace> std;> int> main()> {> >// nesting of try/catch> >try> {> >try> {> >throw> 20;> >}> >catch> (>int> n) {> >cout <<>'Handle Partially '>;> >throw>;>// Re-throwing an exception> >}> >}> >catch> (>int> n) {> >cout <<>'Handle remaining '>;> >}> >return> 0;> }> |
>
>Produktion
Handle Partially Handle remaining>
En funktion kan också kasta om en funktion med samma kast; syntax. En funktion kan hantera en del och be den som ringer att hantera det återstående.
Fastighet 6
När ett undantag kastas, förstörs alla objekt som skapats inuti det omslutande försöksblocket innan kontrollen överförs till fångstblocket.
Exempel
Följande program demonstrerar ovanstående egenskap.
CPP
// C++ program to demonstrate> #include> using> namespace> std;> // Define a class named Test> class> Test {> public>:> >// Constructor of Test> >Test() { cout <<>'Constructor of Test '> << endl; }> >// Destructor of Test> >~Test() { cout <<>'Destructor of Test '> << endl; }> };> int> main()> {> >try> {> >// Create an object of class Test> >Test t1;> >// Throw an integer exception with value 10> >throw> 10;> >}> >catch> (>int> i) {> >// Catch and handle the integer exception> >cout <<>'Caught '> << i << endl;> >}> }> |
>
>Produktion
Constructor of Test Destructor of Test Caught 10>
Begränsningar för undantagshantering i C++
Undantagshanteringen i C++ har också några begränsningar:
- Undantag kan bryta strukturen eller flödet av koden eftersom flera osynliga utgångspunkter skapas i koden vilket gör koden svår att läsa och felsöka.
- Om undantagshanteringen inte görs korrekt kan det också leda till resursläckor.
- Det är svårt att lära sig hur man skriver undantagskod som är säker.
- Det finns ingen C++-standard för hur man använder undantagshantering, därför finns det många variationer i undantagshanteringsmetoder.
Slutsats
Undantagshantering i C++ används för att hantera oväntade händelser med hjälp av försök och fånga block för att hantera problemet effektivt. Denna undantagshantering gör våra program mer tillförlitliga eftersom fel under körning kan hanteras separat och det hjälper också till att förhindra att programmet kraschar och abrupt avslutas av programmet när ett fel uppstår.
Relaterade artiklar:
- Bästa C++ undantagshantering intervjufrågor och svar
- Frågesport om undantagshantering i C++