C++ The order in which exceptions are thrown and received
- 2020-11-18 06:22:45
- OfStack
Exceptions (exception) are the error handling mechanism introduced by the C++ language. It adopts the way of unity 1 to deal with the runtime errors of the program, which has the characteristics of standardization, safety and efficiency. In order to implement exception handling, three keywords are introduced: try, throw and catch. Exceptions are thrown by throw in the format throw[expression] and caught by catch. An Try block is a block of statements that can throw an exception, and it usually occurs in succession with one or more catch blocks.
try statement blocks and catch statement blocks must work together, causing a compilation error in any of the following three cases:
(1) Only try statement block without catch statement block, or only catch statement block without try statement block;
(2) There are other statements between try statement block and catch statement block;
(3) When try statement block is followed by multiple catch statement blocks, other statements are intersperded between catch statement blocks;
(4) The transfer value catch branch and the transfer reference catch branch of the same data type cannot appear at the same time.
During the process of throwing and receiving exceptions, there are several other points to note.
1. When are thrown exception objects destroyed?
When an object is thrown with the throw statement, a new object is constructed, and this object is the exception object. The object's life cycle is calculated from when it is thrown, 1 until it is captured by an catch statement, which is destroyed when the catch block completes execution. Consider the following procedure.
#include <iostream>
using namespace std;
class ExClass
{
int num;
public:
ExClass(int i)
{
cout<<"Constructing exception object with num="<<i<<endl;
num=i;
}
ExClass(ExClass& e)
{
cout<<"Copy Constructing exception object with num="<<e.num+1<<endl;
num=e.num+1;
}
~ExClass()
{
cout<<"Destructing exception object with num="<<num<<endl;
}
void show()
{
cout<<"the number is "<<num<<endl;
}
};
int main()
{
ExClass obj(99);
try
{
throw obj; // Results in output: Constructing exception object with num=100
}
catch(double f)
{
cout<<"exception catched"<<endl;
}
// Results in output: Constructing exception object with num=101
catch(ExClass e)
{
e.show();
}
cout<<"after catch"<<endl;
}
The output result of the program is:
[
Constructing exception object with num=99
Copy Constructing exception object with num=100
Copy Constructing exception object with num=101
the number is 101
Destructing exception object with num=101
Destructing exception object with num=100
after catch
Destructing exception object with num=99
When an object is thrown with the throw statement, a new object is constructed, and this object is the exception object. The object's lifetime is calculated from when it is thrown, 1 until it is captured by an catch statement, which is destroyed after the catch block is executed. In the above program, the num value of the exception object is 100. "Destructing exception object with num=100" is printed before "after catch", which indicates that the destruction time of the exception object is after the execution of the catch block it was captured.
So the catch branch executes like a function call, with the arguments of catch equivalent to the formal parameters of the function, and the exception object thrown equivalent to the arguments of the function call. When the form-participating arguments are successfully matched, the exception is caught by an catch branch. The arguments after catch can only be passed by value, by reference, and by pointer. If the argument is passed by value, a copy of the argument will be generated, and if the argument is an object, the constructor will be called. In the above program, to execute the catch(ExClass e) statement is to construct an object, e, from the exception object, so the copy constructor is called.
Note that the catch branch and the catch branch of the same data type cannot be present at the same time.
2. What happens if the current function is not caught?
In some cases, all catch branches may fail to catch the exception thrown, which results in the end of the current function execution and the return to the calling function. In the calling function, the above process of catching exceptions continues until the exception is caught or the entire program is eventually terminated. Consider the following procedure.
#include <iostream>
using namespace std;
class ExClass
{
int num;
public:
ExClass(int i)
{
cout<<"Constructing exception object with num="<<i<<endl;
num=i;
}
ExClass(ExClass& e)
{
cout<<"Copy Constructing exception object with num="<<e.num+1<<endl;
num=e.num+1;
}
~ExClass()
{
cout<<"Destructing exception object with num="<<num<<endl;
}
void show()
{
cout<<"the number is "<<num<<endl;
}
};
void throwExFunc()
{
try{
throw ExClass(199);
}
catch(double f){
cout<<"double exception catched"<<endl;
}
cout<<"exit throwExFunc()"<<endl;
}
int main()
{
try
{
throwExFunc();
}
catch(ExClass e)
{
e.show();
}
catch(...)
{
cout<<"all will fall in"<<endl;
}
cout<<"continue to execute"<<endl;
}
Output results of the program:
[
Constructing exception object with num=199
Copy Constructing exception object with num=200
the number is 200
Destructing exception object with num=200
Destructing exception object with num=199
continue to execute
As can be seen from the results of the program:
(1) The exception object that was thrown had an num value of 199, and because it was not caught in the function throwExFunc(), it caused the execution of throwExFunc() to end (otherwise it would have printed out: exit throwExFunc()). In the main() function, catch(ExClass e) captures the exception object and produces the object e by copying the constructor. The num value of e is 200. After the catch statement block runs, the object e is destroyed first, followed by the exception object. After this, the program continues to run, output: continue to execute.
(2) catch (...). All types of exceptions can be caught. The casual use of catch(...) is not recommended. , because this leads to inaccurate handling of exception types and reduces the efficiency of the program. However, during the development phase of the program, catch(...) Still useful, because if you go to catch(...) after careful exception capture Statement block, indicating that the previous code is defective and needs to be corrected one step further.
(3) When catching exception objects, you can also use the method of passing references, such as catch statement written as catch(ExClass) & e), so you don't have to produce copies of exception objects, reduce the running overhead of the program, improve the running efficiency.
(4) When throwing an exception, a pointer can also be thrown. This is not always safe, of course. If you want to ensure security, you should either point to a global (static) object or to a dynamically requested space, or the thrown pointer is captured within this function. Otherwise, it is dangerous to use a thrown pointer to an object that has been destroyed. To do so, first, you must ensure that the object's destructor does not make damaging changes to the object's contents, and second, that the object's space is not overwritten by other newly generated variables. That is, even though the object is released, its valid contents remain on the stack.
That's the order in which C++ throws and receives exceptions. For more information on C++ throws and receives exceptions, please follow the other articles on this site!