C + + language Learning (18)--Exception handling one, C language exception handling
An exception is a program that produces a predictable execution branch during operation. For example, except for 0 operation, the array access is out of bounds and the file to be opened does not exist.
A bug is an error in a program that is not expected to run in a way. If the wild pointer, heap space use End is not released.
The way in which exceptions are handled in the C language is generally the use of if....else ... The branch statement.
double divide(double a, double b){ const double delta = 0.000000000000001; double ret = 0; if( !((-delta < b) && (b < delta)) ) { ret = a / b; } else { cout << "a is devieded by zero" <<endl; } return ret;}
The C language optimizes exception handling through setjmp and longjmp.
int setjmp(jmp_buf env);
To save the context to the JMP_BUF structure
void longjmp(jmp_buf env, int value);
Restores the setjmp-saved context from the jmp_buf struct, and eventually returns from the SETJMP function call point, which is the value returned.
#include <iostream>#include <csetjmp>using namespace std;static jmp_buf env;double divide(double a, double b){ const double delta = 0.000000000000001; double ret = 0; if( !((-delta < b) && (b < delta)) ) { ret = a / b; } else { longjmp(env, 1); } return ret;}int main(int argc, char *argv[]){ if( setjmp(env) == 0 ) { double r = divide(1, 1); cout << "r = " << r << endl; } else { cout << "Divided by zero..." << endl; } return 0;}
Second, C + + language exception handling mechanism 1, exception handling introduction
The syntax for exception handling is built into the C + + language, Try.....catch ....
The TRY statement block is used to handle normal code logic, and the catch statement block is used to handle exception handling situations and throw throws an exception. The exception that is thrown in the try statement block captures processing in the corresponding Catch statement block.
The same try statement block can correspond to more than one catch statement block, the CATCH statement block can define the type of exception to be handled, different types of exceptions are handled by different catch statement blocks, the try statement block can throw any type of exception, catch (...) For handling all types of exceptions, any exception can only be captured once.
The exception thrown by the throw must be caught, if the current function can handle the exception, continue execution, and if the current function cannot handle the exception, the function stops executing and returns. Unhandled exceptions are passed up the function call stack until they are processed, or the program stops executing.
Match exception Handling:
A, exception throws from top to bottom strictly match each catch statement block processing type, cannot make any type conversion.
B, catch (...) A statement block can only be placed at the last position of a catch statement block branch.
Examples of use of exception handling:
try { throw ‘c‘; } catch(char c) { cout << "catch(char c)" << endl; } catch(short c) { cout << "catch(short c)" << endl; } catch(double c) { cout << "catch(double c)" << endl; } catch(...) { cout << "catch(...)" << endl; }
A catch statement block captures an exception that can be thrown after being re-interpreted, and throws an exception caught in the try...catch of the outer layer.
try { try { throw ‘c‘; } catch(int i) { cout << "Inner: catch(int i)" << endl; throw i; } catch(...) { cout << "Inner: catch(...)" << endl; throw; } } catch(...) { cout << "Outer: catch(...)" << endl; }
Exceptions can often be thrown again after the exceptions caught in the catch statement block are re-interpreted, typically used in engineering practice to unify exception types, such as by capturing exceptions thrown in third-party library functions, and then throwing uniform exception handling information after being re-interpreted.
The type of the exception can be a custom type, and the exception match for the custom type is still a top-down strict match, but because the assignment compatibility principle applies in the exception match, the catch statement block that matches the subclass exception is placed at the top of the Catch branch, The catch statement block that matches the parent exception is placed in the lower part of the catch branch.
#include <iostream>using namespace std;class Parent{public: Parent(int i):code(i) { }private: int code;};class Child : public Parent{public: Child(int i):Parent(i),code(i) { }private: int code;};int main(int argc, char *argv[]){ try { Child child(1); throw child; } catch(const Child& e) { cout << "catch(const Child& e)" << endl; } catch(const Parent& e) { cout << "catch(const Parent& e)" << endl; } return 0;}
2. Exception Handling in STL
STL provides a practical exception handling class, STL exceptions are inherited from the exception class, exception class as long as there are two branches, Logic_error and Runtime_error. Logic_error is used in handlers to avoid logic errors, and runtime_error is used to handle malignant errors that cannot be handled in a program.
#include <iostream>#include <stdexcept>using namespace std;int main(int argc, char *argv[]){ int array[5] = {0}; for(int i = 0; i < 5; i++) { array[i] = i; } try { for(int i = 0; i < 10; i++) { if(i >= 5) { throw out_of_range("out of range"); } else { cout << array[i] <<endl; } } } catch(const out_of_range& e) { cout << e.what() << endl; } return 0;}
3, Try...catch Special grammar
The Try...catch statement is used to separate normal function code from exception handling code. The Try...catch statement can also separate the function body into two parts.
function declarations and definitions can directly specify the types of exceptions that can be thrown, and exception declarations as part of a function can improve code readability.
A function exception declaration is a contract with a compiler that can only throw a declared exception when the function declares an exception. Throwing other exceptions will cause the program to terminate. You can also define no exception functions through function exception declarations.
#include <iostream>using namespace std;//声明抛出的异常类型为intvoid func(int i, int j)throw(int){ if(0 < j && j < 10) { } else { throw 0; }}void test(int i)try{ func(i,i);}catch(int i){ cout << "catch(int i): " << i << endl;}catch(...){ cout << "Exception:" << endl;}int main(int argc, char *argv[]){ test(10); test(1); return 0;}
In the preceding code, the Func function declares that the exception type thrown is int, so the Func function can only throw an int type exception, and if throwing other types of exceptions will cause the program to run to terminate. Even if the test function captures other types of exceptions that are thrown, the program runs and terminates.
If more than one type of exception can be thrown inside a function, you need to specify the declared exception type when the function declares the exception, with the following code:
#include <iostream>#include <string>using namespace std;//声明抛出的异常类型为int,char,stringvoid func(int i, int j)throw(int,char,string){ if(0 < j && j < 10) { throw j; } if(10 < j && j < 100) { throw ‘A‘; } else { throw string("string exception."); }}void test(int i)try{ func(i,i);}catch(int i){ cout << "catch(int i): " << i << endl;}catch(char c){ cout << "Exception:" << c << endl;}catch(string s){ cout << s << endl;}catch(...){ cout << "Exception:" << endl;}int main(int argc, char *argv[]){ test(115);//string exception. test(1);//catch(int i): 1 test(20);//Exception:A return 0;}
In the code above, the Func function can throw multiple types of exceptions, and the test function captures the various exception types thrown by the Func function.
4. Unhandled exception
If the exception is not handled, the Terminate function is automatically called. The Terminate function is the last chance for the entire program to release system resources. By default, the Terminate function calls the Abort library function to terminate the program. The Abort function causes the program to execute abnormally and exits immediately.
#include <iostream>using namespace std;class Test{public: Test() { cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; }};int main(int argc, char *argv[]){ static Test test; throw 1; return 0;}
The result of the above code operation is as follows:
C + + supports replacing the default terminate function implementation with a custom terminate function implementation.
The implementation rules for custom terminate functions are as follows:
A, customize a function with no return value, no parameter
B, cannot throw any exception
C. The current program must be terminated in some way
You can set a custom terminate End function by calling the Set_terminate function, which uses the following:
A, the parameter type is void (*) ()
B, the return value is the default Terminate function entry address
#include <iostream>#include <cstdlib>using namespace std;class Test{public: Test() { cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; }};void terminate_test(){ cout << "void terminate_test()" << endl; exit(1);}int main(int argc, char *argv[]){ set_terminate(terminate_test); static Test test; throw 1; return 0;}// output:// Test()// void terminate_test()// ~Test()
The above code calls exit (1) in the final Terminate_test function, and the Exit function ensures that the objects of the global and static data extents in the program are destroyed correctly. If you replace the Exit function with the Abort function, the program runs the following result:
Throwing an exception in the destructor may cause the final End function to be called repeatedly by the Terminate function.
5, the function of the exception specification description
The C + + language provides a syntax declaration that declares a function to throw an exception. An exception declaration is the modifier of a function declaration, which is located after the function parameter table. An example of a function exception declaration is as follows:
//可能抛出任何异常void func1();//只能抛出的异常类型:char,intvoid func2() throw(char, int);//不抛出任何异常void func3() throw();
The meaning of the
Function exception declaration is as follows:
A, prompting the function caller to be prepared for exception handling
B, prompting the maintainer of the function not to throw other exceptions
C, function exception specification is part of the function interface
If the function throws an exception type that is not in the function exception declaration, The global Unexpected () function is called. The default unexpected () function calls the global terminate function, which can replace the default unexpected () function implementation with a custom function. The implementation rules for the
Custom Unexpected () function are as follows:
A, a custom non-return value, a parameterless function
B, the ability to throw an exception again, and when the exception conforms to the exception specification of the triggering function, the recovery program executes. Otherwise, call the global terminate function to end the program.
by calling the set_unexpected function, you can set a custom unexpected () function using the following:
A, the parameter type is void (*) ()
B, and the return value is the default unexpected () function entry address.
#include <iostream>#include <cstdlib>using namespace std;void func() throw(int){ cout << "void func()throw(int)" << endl; throw ‘A‘;}void unexpected_test(){ cout << "void unexpected_test()" << endl; throw 1;}int main(int argc, char *argv[]){ set_unexpected(unexpected_test); try { func(); } catch(int) { cout << "catch(int)" << endl; } catch(char) { cout << "catch(char)" << endl; } return 0;}// output:// void func()throw(int)// void unexpected_test()// catch(int)
The C + + compiler does not necessarily support the function exception specification in the C + + language. VC + + compiler is not supported, g++ compiler support.
6. Dynamic Memory Request exception
In the C language, the malloc function returns a null value if it fails to request memory.
In the C + + language, a null value is returned for the earlier C + + compiler when the new keyword requests memory failure, and a Std::bad_alloc exception is thrown when the new keyword requests memory failure for the modern C + + compiler.
The standard behavior of the new keyword in the C + + language specification is as follows:
A, new in memory allocation, if there is not enough space, the global new_handler function will be called, the New_handler function throws a Std::bad_alloc exception, if successful, the constructor is called in the allocated space to create the object, and return the address of the object.
B. You can customize the New_handler function to handle the failure of the default new memory allocation.
#include <iostream>#include <cstdlib>using namespace std;void new_handler_test(){ cout << "void new_handler_test()" << endl; cout << "No enough memory" << endl; exit(1);}int main(int argc, char *argv[]){ set_new_handler(new_handler_test); int* p = new(std::nothrow) int[10000000000]; return 0;}// output:// void new_handler_test()// No enough memory
In the code above, the custom New_handler function is called when an exception is thrown.
#include <iostream> #include <cstdlib> #include <new>using namespace std;void new_handler _test () {cout << "void New_handler_test ()" << Endl; cout << "No enough Memory" << Endl; Exit (1);} int main (int argc, char *argv[]) {new_handler func = Set_new_handler (new_handler_test); cout << "func =" << func << Endl; if (func) {try {func (); } catch (const bad_alloc& e) {cout << e.what () << Endl; }} return 0;} Func = 0
The above code is in the g++ compiler, VC + + compiler compiled after the results of printing, indicating that g++ compiler, VC + + compiler does not set the default New_handler function. If the C + + compiler (such as the BCC compiler) is set with a default New_handler function, the Func function throws a Bad_alloc exception and prints out information about the BAD_ALLOC exception when it is captured.
different C + + compilers, which behave differently when the new keyword requests dynamic memory failure.
in engineering practice, in order to unify the behavior of the new keyword among different C + + compilers, and to improve the portability of the code, the solution is as follows:
A, redefine the global New/delete implementation without throwing any exceptions, and customize the New_handler function, Do not throw any exceptions (not recommended).
B, overloading the new/delete operator within a class, without throwing any exceptions.
C, a single dynamic memory allocation using the Nothrow parameter, indicating that new does not throw an exception.
#include <iostream> #include <cstdlib> #include <new>using namespace std;class test{int m_data;public : Test () {cout << "test ()" << Endl; m_data = 0;//Exception} ~test () {cout << "~test ()" << Endl; } void* operator new (unsigned int size) {cout << "operator NEW:" << size << Endl; return malloc (size); return NULL; } void operator Delete (void* p) {cout << "operator Delete:" << p << Endl; Free (p); } void* operator new[] (unsigned int size) {cout << "operator new[]:" << size << Endl; return malloc (size); return NULL; } void operator delete[] (void* p) {cout << "operator delete[]:" << p << Endl; Free (p); }};int Main (int argc, char *argv[]) {test* p = new Test (); cout << p << Endl; Delete p; return 0;}output://operator new:4//Test ()//exception
The code above calls the test constructor after executing the new operator function and throws an exception when initializing the M_DATA member variable. To ensure that different C + + compilers have the same behavior when invoking the new keyword, you need to not throw an exception when new fails, so you need to increment the function's exception declaration in the new operator.
#include <iostream> #include <cstdlib> #include <new>using namespace std;class test{int m_data;public : Test () {cout << "test ()" << Endl; m_data = 0;//Exception} ~test () {cout << "~test ()" << Endl; } void* operator new (unsigned int size) throw () {cout << "operator NEW:" << size << Endl ; return malloc (size); return NULL; } void operator Delete (void* p) {cout << "operator Delete:" << p << Endl; Free (p); } void* operator new[] (unsigned int size) throw () {cout << operator new[]: "<< size << Endl return malloc (size); return NULL; } void operator delete[] (void* p) {cout << "operator delete[]:" << p << Endl; Free (p); }};int Main (int argc, char *argv[]) {test* p = new Test (); cout << p << Endl; DeleteP p = new Test[5]; cout << p << Endl; delete [] p; return 0;} output://operator new:4//0//operator new[]: 24//0
The code above overloads the new and delete keywords of the test class to unify the behavior of new when it fails.
#include <iostream>using namespace std;int main(int argc, char *argv[]){ //不抛出异常 int* p = new(nothrow) int[1000000000]; cout << "p = " << p << endl; delete [] p; int array[2] = {0}; struct Test { int x; int y; }; //在栈空间创建对象 Test* pTest = new(array) Test(); pTest->x = 100; pTest->y = 200; cout << array[0] << endl; cout << array[1] << endl; //显示析构 pTest->~Test(); return 0;}// output:// p = 0// 100// 200
The code above uses the Nothrow keyword object new to make sure that the new creation object fails without throwing an exception. The New keyword can also specify an address space for creating objects, such as stack space.
7. C + + compiler implementation of the New keyword
Not all C + + compilers follow the C + + standard specification, and the C + + compiler may redefine the implementation of the new keyword and throw a Bad_alloc exception in the implementation. The VC + + compiler redefined the new keyword, and the new keyword was implemented in the New.cpp file.
#ifdef _syscrt#include <cruntime.h> #include <crtdbg.h> #include <malloc.h> #include <new.h># Include <stdlib.h> #include <winheap.h> #include <rtcsup.h> #include <internal.h>void * operator new (size_t cb) {void *res; for (;;) {//Allocate memory block res = _HEAP_ALLOC (CB); If successful allocation, return pointer to memory if (res) break; Call installed new handler if (!_CALLNEWH (CB)) break; New handler is successful--try to allocate again} rtccallback (_rtc_allocate_hook, (res, CB, 0)); return res;} #else/* _SYSCRT */#include <cstdlib> #include <new>_c_lib_declint __cdecl _callnewh (size_t size) _throw1 (_ STD bad_alloc); _end_c_lib_declvoid *__crtdecl operator new (size_t size) _throw1 (_std bad_alloc) {//try to a Llocate size bytes void *p; while ((p = malloc (size)) = = 0) if (_CALLNEWH (size) = = 0) {//Report no memory static const STD::BAD_ALLOC NOMEM; _raise (NOMEM); } return (P); }/* * Copyright (c) 1992-2002 by P.J. Plauger. All rights RESERVED. * Consult your license regarding permissions and restrictions. v3.13:0009 */#endif/* _SYSCRT */
The code above shows that the Bad_alloc exception is thrown by default when new fails.
C + + language Learning (18)--Exception handling