1. Why C + + does not like destructors throw exceptions
C + + does not prevent the destructor from appearing abnormally, but it certainly does not encourage this. This is for a reason, consider the following code:
1 class Widget {
2
3 public:
4
5 ...
6
7 ~widget () {...}//Assume this might emit a exception
8
9};
A
void dosomething ()
std::vector<widget> v;
...
destroyed//V is automatically-here
When Vector v is destroyed, V has the responsibility to destroy all the widgets it contains. Assuming v contains 10 widgets objects, an exception is thrown when the first widgets object is destroyed. The remaining 9 still need to be released (otherwise the resources they own will be compromised), so v should trigger all destructors for the remaining 9 objects. But suppose that the destructor of the second widget throws an exception during the 9 destructor calls. Now there are two of unsolicited exceptions, which is too much for C + +. in cases where two exceptions occur at the same time, the execution of the program either terminates or results in undefined behavior . In this case, it produces undefined behavior. Using any other standard sink, such as a list or set, or a container in a TR1, even an array will produce the same undefined behavior. This kind of trouble occurs not just in containers or arrays. An exception thrown by a destructor can also cause a program to terminate prematurely or to have undefined behavior without using a container or an array. C + + does not like destructors to emit exceptions. 2. One example-DB resource management class
This is easy to understand, but the action that the destructor needs to perform is likely to fail due to an exception being thrown, and what we should do. For example, let's say you're implementing a class about database connections:
1 class DbConnection {
2
3 public:
4
5 ...
6
7 static dbconnection Create ();//function to return
8
9//DbConnection objects; params A/
o Mitted for simplicity
-close ();//close connection; throw;
//exception if Closi Ng fails
In order to ensure that the client does not forget to invoke the close function of the DbConnection object, creating a resource management class for Dbconnestion is an ideal method, and the close function is invoked in the destructor of the resource management class. Such a resource management class will be covered in detail in chapter three, where it is sufficient to consider what a destructor of such a class would look like:
1 class Dbconn {//class to manage DbConnection
2
3 public://Objects
4
5 ...
6
7 ~dbconn ()//Make sure database connections
8
9 {//are always closed
() db.close >12
private:
dbconnection db;
19};
So the client code can be written like this:
1 {//Open a block
2
3 dbconn DBC (Dbconnection::create ());//Create DbConnection Object
4
5//and turn It over to a dbconn
6
7//object to manage
8
9 ...//Use the DbConnection object
/via th E dbconn interface
//At end of blocks, the dbconn
//object is destroyed, thus
16
17//Automatically calling close on
//The DbConnection object
This implementation is good as long as the call to the close function succeeds, but if the call produces an exception, the Dbconn destructor propagates the exception, which allows the exception to leave the destructor. This is a problem because the occurrence of throw in a destructor means that it is troublesome. 3. How to prevent exceptions from destructors from being propagated
There are two ways to avoid this problem. Dbconn's destructor can do this: 3.1 terminate the program with the Abort function
If the close function terminates the program by throwing an exception, you can call the Abort function:
1 Dbconn::~dbconn ()
2
3 {
4
5 try {db.close ();}
6
7 catch (...) {
8 9 make log entry this call to close
failed;
Std::abort ();
15}
If you run into an error program while executing a destructor, the above procedure is a reasonable choice. It has the advantage of being able to prevent exceptions from being propagated from destructors, and propagating exceptions can lead to undefined behavior. Therefore, for undefined behavior, invoking abort can be preemptive. 3.2 will be swallowed abnormally
The exception that is thrown when you call Close is swallowed
1 Dbconn::~dbconn ()
2
3 {
4
5 try {db.close ();}
6
7 catch (...) {
8 9 make log entry this call to close
failed;
13}
In general, it is a bad way to swallow an exception because it suppresses important error messages-There are some failed things-appearing. But sometimes it's better to swallow an exception than if the program terminates prematurely or undefined behavior. This is a viable option, and the program must be able to continue to execute reliably even when the error occurs and then ignores it.
Neither of these methods is particularly appealing. The problem with both methods is that the program has no way to react to the condition that caused close to throw an exception at the first time.
4. A better way-to enable classes to react to exceptions
A better approach is to design the Dbconn interface, so the client has the opportunity to react to problems that may arise. For example, the Dbconn class can provide a close function itself, which gives the client an opportunity to handle an exception from close, while also tracking whether the dbconnection has been turned off and executing again in the destructor if it is not turned off in close. This prevents the connection from being properly freed. If a call to close in a dbconn destructor fails, we also have to use the Terminate program or swallow the exception:
1 class Dbconn {2 3 public:4 5 ... 6 7 void Close ()//new function for 8 9 {//client use Db.close () closed = true; ~db
Conn () {if (!closed) {n ' try {//Close the connection db.close ()//If the client didn ' t 26 catch (...) {//If closing fails, make log entry this call to close failed;//The note that and the ...//terminate or Swallo
(w) (private:42)-DbConnection db;
closed bool;
The
The responsibility to call close is transferred from the Dbconn destructor to the Dbconn client (since Dbconn's destructor has a "backup" call) may give you the impression of reckless transfer of the burden. You may even use this practice as a Item18 to give advice (make the interface easy to work with). In fact, both of these ideas are wrong. If an operation might fail because it throws an exception, and we might need to handle the exception, the exception must come from a non destructor. Because a destructor throws an exception is dangerous, it often causes the program to terminate prematurely or undefined behavior. In this example, telling the client to call the close function on their own does not add to their burden; This gives them an opportunity to deal with the error, otherwise there will be no chance to respond to the error. If they find this opportunity useless (probably because they believe no errors will occur), they can ignore it and rely only on the Dbconn destructor to call close. If there is an error,-close does throw an exception-they are not qualified to complain that dbconn swallowed the exception or terminated the program. After all, they had a chance to deal with the problem, but they didn't. 5. Summary The destructor is not able to emit any exceptions. If a function is invoked in a destructor, throw can occur, and the destructor should catch all exceptions and then swallow them or terminate the program. If the client of a class needs to react to an exception throw of an operation, the class should provide a normal function to perform this operation.