[Translation] semantic tive C ++, 3rd edition, item 8: prevents Destructors from being removed due to exceptions (Exceptions)

Source: Internet
Author: User

Item 8: prevents Destructors (destructor) from being removed due to exceptions)

By Scott Meyers

Translator: fatalerror99 (itepub's nirvana)

Release: http://blog.csdn.net/fatalerror99/

C ++ does not prohibit the triggering of exceptions (Exceptions) from Destructors, But it resolutely blocks such practices. For any good reason, consider:

Class widget {
Public:
...
~ Widget () {...} // assume this might emit an exception
};

Void dosomething ()
{
STD: vector <widget> V;
...
} // V is automatically destroyed here

When a vector V is destroyed, it has the responsibility to destroy all widgets it contains. Assume that V contains ten widgets, and an exception is thrown during the first destructor ). The other nine widgets must still be destroyed (otherwise all the resources they hold will be leaked), So V should call their Destructors (destructor ). However, during this call, The Destructors (destructor) of the second widget throws an exception ). Currently, there are two events with exceptions. For C ++, this is too much. The execution of the program terminates or triggers undefined behavior (undefined behavior) If two exceptions occur simultaneously under a coincidence ). In this example, undefined behavior (undefined behavior) is triggered ). Use any other standard library container (container) (such as list, set), the container (container) in any tr1 (see item 54), or even an array (array ), both may cause the same undefined behavior (undefined behavior ). It is not necessary to be a containers (container) or arrays (array) to be in trouble. Premature termination of the program or undefined behavior (undefined behavior) is the result of the exceptions (exception) caused by Destructors, even if the containers (container) and arrays (array) are not used) and so on. C ++ does not like Destructors that causes exceptions (exceptions ).

This is easy to understand, but what if your destructor (destructor) needs to execute an operation that may fail and throw an exception? For example, suppose you work with a database connection class:

Class dbconnection {
Public:
...

Static dbconnection create (); // function to return
// Dbconnection objects; Params
// Omitted for simplicity

Void close (); // close connection; throw
}; // Exception if closing fails

To ensure that the customer will not forget to call close on dbconnection objects (object), a reasonable idea is to create a resource-management class (resource management class) for dbconnection ), call close in its destructor (destructor. This kind of resource-management classes will be explored in Chapter 3 (Chapter 3), but here, you only need to consider such a class) the Destructor (destructor) of is enough to look like this:

Class dbconn {// class to manage dbconnection
Public: // objects
...
~ Dbconn ()// Make sure database connections
{// Are always closed
DB. Close ();
}
PRIVATE:
Dbconnection dB;
};

It allows users to program like this:

{// Open a block
Dbconn DBC (dbconnection: Create (); // create dbconnection object
// And turn it over to a dbconn
// Object to manage
... // Use the dbconnection object
// Via the dbconn Interface
} // At end of block, the dbconn
// Object is destroyed, thus
// Automatically calling close on
// The dbconnection object

You only need to call close successfully. However, if this call causes an exception, the dbconn destructor (destructor) will spread the exception, that is, it will leave destructor (destructor ). This creates a problem because destructor throws a hot potato.

There are two main ways to avoid this problem. Dbconn's destructor (destructor) can:

  • Terminate the programIf close throws (if close throws an exception, terminate the Program), generally by calling ABORT:

Dbconn ::~ Dbconn ()
{
 Try {dB. Close ();}
 Catch (...){
Make log entry that the call to close failed;
STD: Abort ();
 }
}

If the program cannot continue running after an error occurs during the analysis process, this is a reasonable choice. It has the following advantages: an exception that can be propagated from a destructor (destructor) may cause undefined behavior (undefined behavior) to prevent it from occurring. That is to say, calling abort can prevent undefined behavior (undefined behavior) in advance ).

  • Swallow the exceptionArising from the call to close (Suppress the exception caused by this close call ):

Dbconn ::~ Dbconn ()
{
 Try {dB. Close ();}
 Catch (...){
Make log entry that the call to close failed;
 }
}

Swallowing exceptions is generally a bad idea because it will conceal important information --Something failed(Something failed )! However, sometimes swallowing exceptions are more desirable than the risk of premature termination of a program or undefined behavior (undefined behavior. The program must be able to run reliably after it encounters an error and ignores it. This can be a feasible choice.

These methods are not very attractive. Their first problem is that the program cannot respond to conditions that cause close to throw an exception.

A better strategy is to design dbconn interfaces so that its customers can respond to possible problems. For example, dbconn can provide a close function to give the customer a chance to handle exceptions (Exceptions) from that operation ). It can also maintain whether its dbconnection has been tracked by closed. If not, it will be disabled in destructor (destructor. This prevents connection leakage. If the call to close fails in the Destructor (destructor) of dbconnection (original, we can return to termination or suppression.

Class dbconn {
Public:
...

Void close ()// New function
{// Client use
DB. Close ();
Closed = true;
}

~ Dbconn ()
{
If (! Closed ){
Try {// Close the connection
DB. Close ();// If the client didn't
}
Catch (...){// If closing fails,
Make log entry that call to close failed;// Note that and
...// Terminate or swallow
}
}

PRIVATE:
Dbconnection dB;
Bool closed;
};

Hand over the call close responsibility from dbconn's destructor (destructor) to dbconn's customers (and include an "alternate" call in dbconn's destructor) it may surprise you as an unshirking responsibility. You can even think of it as a violation of the suggestion in item 18 about making interfaces (Interface) Easy to use correctly. In fact, this is not true. If an operation may fail and an exception (exception) is thrown, and it may be necessary to handle this exception (exception), this exception (exception) willHas to come from some non-destructor function(Must come from non-destructor ). This is because the Destructor (destructor) causes exception, which is always at the risk of premature termination of the program or undefined behavior (undefined behavior. In this example, asking the customer to call close does not impose a burden on them, but gives them a time to handle errors. Otherwise, they will not have a chance to respond. If they cannot find the opportunity (maybe because they believe there will be no errors), they can ignore it and call close for them by dbconn's destructor (destructor. If an error occurs exactly at that time -- if it is thrown by close -- If dbconn suppresses that exception or terminates the program, there will be no complaints. After all, they have nowhere to deal with the problem and they will no longer use it.

Things to remember

  • Destructor (destructor) should never cause exceptions (exceptions ). If destructor calls a function that may throw an exception, destructor should capture all exceptions and then suppress them or terminate the program.
  • If the class customer needs to respond to the exceptions (Exceptions) thrown by an operation, the class should provide a common function (that is, non-destructor (non-destructor) to complete this operation.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.