It is enough to read this article for detailed explanation of C++ exception handling

foreword

In the process of program running, we can't guarantee that our program will be 100% free of exceptions and errors, so how to report an error when an exception occurs, and let us know where the error is? C++ provides an exception handling
mechanism .

Primary Keyword for Exception Handling

  • throw : When a problem occurs, the program will throw an exception. This is done using the throw keyword.
  • catch : Catch exceptions via exception handlers where you want to handle the problem. The catch keyword is used to catch exceptions.
  • try : The code in the try block identifies the specific exception that will be activated. It is usually followed by one or more catch blocks (at least one).

When an exception occurs in the code block surrounded by try brackets (we can call it the protected code), an exception will be thrown, and the code in the catch block will catch the exception and execute the code block surrounded by the catch. Because exceptions can also have different types, we can follow try with multiple catch blocks to catch different exceptions, as follows:

try
{
    
    
   // 保护代码
}
catch( ExceptionName1 e1 )//第一种异常
{
    
    
   // catch 块
}
catch( ExceptionName2 e2 )//第二种异常
{
    
    
   // catch 块
}
catch( ExceptionName3 eN )//第三种异常
{
    
    
   // catch 块
}

Throw an exception

Exceptions can be thrown anywhere within a block of code using the throw statement.
The operand of the throw statement can be any expression, and the type of the result of the expression determines the type of exception thrown.
as follows:

#include <iostream>
using namespace std;
double division(double a, double b)
{
    
    
    if (b == 0)
    {
    
    
        throw "出现了除数为0的错误";//throw关键字抛出错误
    }
    return (a/ b);
}
int main()
{
    
    
    cout << division(1, 0);
}

If we run this code in Visual Studio 2019, it will stop the program at the throw and report an error,
If we run this code in Visual Studio 2019, it will report an error at the throw

It can be seen that what we throw is a string of strings, so the error message reported by the compiler says that the exception type is char (actually it is const char*, but it is abbreviated).
When we actually program, we definitely don't want the compiler to just tell me that the exception type is char* and that's it. We also want to print it out, so we can see that "a division by 0 error occurred", which requires catching exceptions.

catch exception

We put the code that will throw an exception into the try code block, and then use the catch keyword to catch the exception. as follows:

#include <iostream>
using namespace std;
double division(double a, double b)
{
    
    
    try
    {
    
    
        if (b == 0)
        {
    
    
            throw "出现了除数为0的错误";//throw关键字抛出错误类型 const char*
        }
    }
    catch (const char* error_string)//捕获const char*类型的错误
    {
    
    
        cout << error_string << endl;//打印它
    }
    return (a / b);
}
int main()
{
    
    
    cout << division(1, 0);
}

Run it in Visual Studio2019, you will see:
output results:

出现了除数为0的错误
inf

An exception of type const char* is printed
It can be seen that with try and catch, the program will not crash and terminate when it encounters an error, but will process it and continue to run.
So this program can still output the calculation result inf (infinity), but it also printed an error message before that.
In short, using the exception handling mechanism, we can handle exceptions while the program is running, greatly reducing the probability of program crashes.

You often encounter the problem of software crashes in your life, and nine out of ten have not handled the exception well.

Exception types that come with the C++ standard library

C++ provides a series of standard exceptions, which are defined in the header file exception, and we can use these standard exceptions in the program. They are organized in a parent-child class hierarchy as follows:
The picture comes from the rookie tutorial
The following table is a description of each exception that occurs in the above hierarchy:

  • std::exception This exception is the parent class of all standard C++ exceptions. std::bad_alloc This exception can be thrown by new.
  • std::bad_cast This exception can be thrown via dynamic_cast. std::bad_typeid This exception can be thrown by typeid
    . std::bad_exception This is useful for handling unexpected exceptions in C++ programs.
  • std::logic_error An exception that could theoretically be detected by reading the code.
  • std::domain_error is thrown when an invalid math domain is used.
  • std::invalid_argument This exception is thrown when an invalid argument is used.
  • std::length_error Thrown when a std::string that is too long is created.
  • std::out_of_range This exception can be thrown by methods such as std::vector and std::bitset<>::operator
  • std::runtime_error is an exception that theoretically cannot be detected by reading the code.
  • std::overflow_error Thrown when mathematical overflow occurs.
  • std::range_error is thrown when an attempt is made to store a value out of range.
  • std::underflow_error Thrown when a mathematical underflow occurs.

exception specification

throw keyword

The purpose of the exception specification is to let the user of the function know what exceptions the function may throw. You can list all exception types that a function may throw in the function declaration. For example:

void fun() throw(A,B,C,D);

In the parentheses after throw, the type of exception that will be thrown must be written. If the parentheses are empty, it means that this function will not throw any exceptions.
If you don't declare throw(A, B, C, D), then the compiler will think that this function may throw any exception.
The exception specification statement can let the compiler know what exceptions may be thrown, which can improve the compilation speed and give the compiler more room for optimization, so that your code performance is higher, and it can also let other programmers understand your code. The code may generate exceptions to improve code readability.

noexcept keyword

The noexcept keyword was added in C++11 to indicate that this function will not throw some kind of exception. And can prevent the propagation of exceptions.

Unconditional noexcept keyword

When the noexcept keyword we declare is unconditional, it means that all codes in this function will not generate exceptions, as follows:

void fun() noexcept; //C++11
void fun() noexcept(); //也可以写成这样,等价的
void fun() noexcept(...); //也可以写成这样,等价的
void fun() noexcept(true); //也可以写成这样,等价的

Conditional noexcept keyword

void fun(Type& x, Type& y) noexcept(noexcept(noexcept(fun1()),noexcept(fun2()));
//表示fun函数内会调用到的fun1函数和fun2函数都不会抛出异常,但不保证其他代码不会抛出异常

When do we need noexcept keyword?

Using noexcept indicates that exceptions will not occur in functions or operations, which will give the compiler more room for optimization. However, adding noexcept does not increase efficiency.
The following situations encourage the use of noexcept:

  • move constructor
  • move assignment function
  • Destructor. Let me mention here that in the new version of the compiler, the destructor is added with the keyword noexcept by default. The following code can detect whether the compiler adds the keyword noexcept to the destructor.
  • Leaf Function. A leaf function means that no stack space is allocated inside the function, no other functions are called, no non-volatile registers are stored, and exceptions are not handled.
    Finally, I would like to emphasize that if it is not the above situation or you are not sure, do not use noexception lightly.

Define new exception types

Only the exception types that come with the C++ standard library are definitely not enough. In our actual work, we need to define new exception types according to project requirements.
In C++, exception types all have a common base class (parent class)—exception class. When we define a new exception, it is best to inherit the exception class. The exception class is defined in the header file exception. When you need to define a new exception Need to #include it, as follows:

#include <iostream>
#include <exception>
using namespace std;
class DivisionZeroException :public exception//基于exception类定义新的异常类型除以0导致的异常类
{
    
    
public:
    //这里重载了父类的虚函数what()
    //throw ()的括号里面没有东西,这表示这个函数不会抛出任何异常
    //const 是常量的关键字,常量在定义后无法被修改
    const char* what() const throw ()
    {
    
    
        return "出现了除以0的错误\n";
    }
};
double division(double a, double b)
{
    
    
    try
    {
    
    
        if (b == 0)
        {
    
    
            DivisionZeroException e;
            throw e;//throw关键字抛出错误类型DivisionZeroException
        }
    }
    catch (DivisionZeroException e)//捕获const char*类型的错误
    {
    
    
        cout<<e.what();//打印什么异常了
    }
    return (a / b);
}
int main()
{
    
    
    cout << division(1, 0);
}

Output result:

出现了除数为0的错误
inf

Exception classes can also be inherited, so in actual software development, we can divide exceptions into several major categories, and then inherit many small classes to handle ever-changing exception types.

Guess you like

Origin blog.csdn.net/lifesize/article/details/128656010