catalogue
2, Exception throwing and matching rules
7, Exception system of C + + standard library
8, Abnormal advantages and disadvantages
preface
The ways and defects of handling errors in C language are:
- Return error code.
Defects:
1. It is difficult to set the error code. For example, it is difficult to return the error code when dividing by 0. If a number is returned, it may have two meanings: error or result. Generally, we will add an output parameter to the parameter to return the result, and then specify the numbers returned for success and failure.
2. Programmers need to find the meaning of the error code. For example, many system interface functions put the error code into the global variable errno to indicate an error.
- Terminate the procedure. For example, in case of out of bounds, division by 0, memory error, etc., the program will be terminated directly.
Defect: you don't know exactly what the mistake is. It is difficult for users to receive.
In view of the above shortcomings, C + + introduces the concept of exception, does not terminate the program, and introduces the error information in detail.
int test(){ int a = 0; int b = 0; cin >> a >> b; if (b == 0){ throw bad_exception("Divide by 0 error");//Throw exception } return a / b; } int main(){ try{ cout << test() << endl;//An exception code may appear } catch (const exception& e){ cout << e.what() << endl;//Catch exception } system("pause"); return 0; }
1, C + + exception concept
Exception is a way of handling errors in object-oriented language. When a function has an error that it cannot handle, it can throw an exception, and then the direct or indirect caller of the input can handle the error.
There are three keywords:
- Throw: when a problem occurs, the program throws an exception. Throwing exceptions is done using the throw keyword.
- Catch: used to catch exceptions. catch(...) can catch any type of exception. It is mainly used to catch exceptions that do not display the capture type. It is equivalent to else in conditional judgment.
- Try: try contains code or functions that will cause exceptions. It is usually followed by one or more catch blocks.
Note: any type of object can be thrown. The exception thrown must be caught. try should be matched with catch. The content in catch is executed only when an exception is thrown. If there is no exception, it will not be executed.
int test(){ int a = 0; int b = 0; cin >> a >> b; if (b == 0){ throw "Divide by 0 error";//Throw exception, exception description } return a / b; } int main(){ try{ cout << test() << endl;//Exception code will appear } //Catch exception catch (const char* a){ cout << a << endl; } catch (...){ cout << "unknow exception" << endl; } system("pause"); return 0; }
2, Exception throwing and matching rules
-
The exception is thrown by throwing an object. The type of the object determines which catch processing code should be activated.
-
The call chain of the selected processing code is the catch that matches the type and is closest to the place where the exception is thrown.
-
After an exception object is thrown, a copy of the exception object will be generated. Because the exception object thrown may be a temporary object, a copy object will be generated.
-
catch(...) can catch any type of object. It is mainly used to catch exceptions that do not display the caught type, because if there is no matching catch, the program will be terminated. It is equivalent to else in conditional judgment. The problem is that you don't know what the exception error is.
-
In practice, the types thrown and captured do not necessarily match exactly. You can throw derived class objects and use base classes to capture them, which is very practical in real life. The main reason is that the derived class can be assigned to the base class.
Exception matching rule:
- First, check whether throw itself is inside the try block. If so, find the matching catch statement in the current function stack. If there is a match, jump directly to the catch.
- If there is no matching catch block, exit the function stack and find the matching catch in the calling function stack.
- If the stack of the main function is reached and there is no matching catch, the program will be terminated.
- The above process of finding matching catch blocks along the call chain is called stack expansion. Therefore, a catch(...) should be added to catch any type of exception to prevent program termination.
- If a matching catch is found, it will directly jump to the execution of the catch statement. After execution, it will continue to execute along the back of the catch statement.
If matched in the middle:
Conclusion: according to the function call chain, look out layer by layer until the matching catch block is found. Directly jump to the matching catch block for execution. After the catch is executed, it will continue to execute the statements behind the catch block. Equivalent to no matching function found, the stack frame is released.
3, Re throw of exception
It is possible that a single catch cannot completely handle an exception. After some correction processing, it needs to be handed over to the call chain function of the outer layer. Catch can complete the correction operation, throw the exception again, and hand it over to the higher-level function for processing.
4, Abnormal security issues
Because of throwing exceptions, as long as a matching catch is found, it will directly jump to the catch block for execution. If the corresponding catch is not found, the function will not continue to be executed. This causes the execution flow back of the function to be chaotic. May cause some problems.
-
The constructor completes the construction and initialization of the object. It is best not to throw exceptions in the constructor, otherwise the object may be incomplete or not fully initialized
-
The destructor is mainly used to clean up resources. It is best not to throw exceptions in the destructor, otherwise memory leakage may occur.
-
C + + exceptions often lead to resource leakage. For example, exceptions are thrown in new and delete, resulting in no release of resources from new, resulting in memory leakage. Throw exceptions in lock and unlock, resulting in no release of the lock and deadlock.
There are two solutions:
- Catch the exception and throw the lock again after releasing the resource.
- Use the idea of RAII to solve. Define a class encapsulation to manage resources. When you want to use, instantiate a class object, pass in resources, and when you exit the function, call the object destructor to release resources.
5, Exception specification
- The exception specification is to make the function caller know what exceptions the function may throw. You can follow the function with throw (exception type) to list all the exception types that this function may throw.
- Add throw() or noexcept after the function to indicate that no exception is thrown.
- If there is no interface declaration, this function may throw any type of exception.
// This means that this function will throw some type of exception in A/B/C/D void fun() throw(A,B,C,D); // This means that this function will only throw bad_alloc exception void* operator new (std::size_t size) throw (std::bad_alloc); // This means that this function will not throw an exception void* operator new (std::size_t size, void* ptr) throw(); void* operator new (std::size_t size, void* ptr) noexcept;
6, Custom exception system
In practice, we don't throw any exceptions as we want, which will make it difficult to catch. Instead, an inheritance system will be established, an exception class will be established, and the derived class will inherit this class to define different exceptions.
When the exception is thrown, you only need to catch it with the base class.
The base class can be equivalent to a framework, and the derived class is a concrete exception. Then implement the content of the exception, throw the exception only by throwing the derived class, and catch the exception only by catching the base class.
Here is a simple implementation:
//Base class //abnormal class Exception{ public: Exception(const char* str = nullptr, int id = 0) :_errmsg(str) , _id(id) {} virtual void what()const = 0; protected: string _errmsg;//error message int _id;//Error code }; //Derived class //Database exception class SqlException :public Exception{ public: SqlException(const char *str = nullptr, int id = 1) :Exception(str, id) {} virtual void what()const{ cout << "error msg:" << _errmsg << endl; cout << "error id:" << _id << endl; } }; //Network exception class HttpException :public Exception{ public: HttpException(const char *str = nullptr, int id = 2) :Exception(str, id) {} virtual void what()const{ cout << "error msg:" << _errmsg << endl; cout << "error id:" << _id << endl; } }; //Cache exception class CacheException :public Exception{ public: CacheException(const char *str = nullptr, int id = 3) :Exception(str, id) {} virtual void what()const{ cout << "error msg:" << _errmsg << endl; cout<< "error id:" << _id << endl; } }; void test(){ //When the network connection fails, throw this exception //throw HttpException("Http fail", 2); //When cache error //throw CacheException("Cache error", 3); //When database error throw SqlException("Sql error", 4); } int main(){ try{ test(); } //Capture with base class catch (const Exception& a){ a.what(); } catch (...){ cout << "unknow exception" << endl; } system("pause"); return 0; }
7, Exception system of C + + standard library
An exception system is also established in the C + + library. It also provides us with some exception classes. We can use these standard exceptions in programs, and they are also organized in a parent-child class hierarchy.
explain:
unsigned short test(){ unsigned short a = 0; unsigned short b = 0; cin >> a >> b; if (a + b > 65535 ){ //Exception classes in c + + standard library throw overflow_error("overflow"); } return a + b; } int main(){ try{ printf("%d\n", test()); } //Capture with base class catch (const exception& a){ cout << a.what() << endl; } catch (...){ cout << "unknow exception" << endl; } system("pause"); return 0; }
8, Abnormal advantages and disadvantages
Exceptional advantages:
- The exception object is well defined. Compared with the error code, it can clearly and accurately display all kinds of error information, even including stack call information, which can help us locate program bug s.
- In the function call chain, if the deep function returns an error, we have to return layer by layer. We need to constantly judge what the error is, and then return it to the outermost layer. The exception will directly find the corresponding catch execution, and there is no need to judge what the error is.
- Some functions are easier to handle, such as functions with no return value or T & operators with their own return value. It is not easy to return error codes. And pos is out of bounds, memory error, etc. there is no need to terminate the program.
- Better testing
In a word, it is better to return and describe the error information. For some errors, there is no need to terminate the program.
Abnormal defect
- Exceptions cause execution flow to jump disorderly and run disorderly. It makes it difficult for us to debug and analyze the program.
- C + + has no garbage collection mechanism, which may lead to abnormal security problems. The opened resources and open streams are not released and closed due to the random jump of the execution stream. Causes a memory leak. The open lock is closed, resulting in deadlock.
- The exception system of C + + standard library is not well defined, which leads to the confusion that we need to customize our own system.
- Throw exceptions at will, and the outer layer is difficult to catch, so try to use it according to the specification for finding exceptions. The exception specification has two points: 1. The derived class inherits the base class. 2. Add how () after the function to indicate what exception to throw.
Conclusion: the advantages of using exceptions outweigh the disadvantages. When exceptions occur, object-oriented language is encouraged to handle errors.