More effective C + +----(14) prudent use of exception specifications (Exception specifications)

Source: Internet
Author: User

Item M14: Careful use of exception specifications (Exception specifications)
There is no doubt that exception specifications are a compelling feature. It makes the code easier to understand because it clearly describes what kind of exceptions a function can throw. But it's not just an interesting comment. The compiler can sometimes detect inconsistencies in the exception specifications at compile time. And if a function throws an exception that is not in the specification range, the system can detect the error at run time, and then a special function unexpected will be automatically called. Exception specifications can be used as a guiding document and an exception to the mandatory constraint mechanism, it seems to have a very attractive appearance.
But in general, beauty is only a skin, the appearance of beauty does not represent its inherent qualities. function Unexpected The default behavior is to call the function terminate, and terminate default behavior is to call the function abort, so a violation of the exception specification of the program its default behavior is halt (stop running). Local variables in the active stack are not freed because abort does not perform such cleanup operations when the program is closed. The violation of the exception specification turned out to be a disaster that should not have happened.
Unfortunately, it's easy to write a function that causes this kind of disaster to happen. The compiler only partially detects if the use of the exception is consistent with the exception specification. One function calls another function, and the latter may throw an exception that violates the former exception specification (a function calls the B function, but because the B function may throw an exception that is not within the specification of a function exception, this function call violates the exception specification of the A function. Translator note) The compiler does not detect this situation, and the language standard also prohibits the compiler from rejecting this invocation (although it can display a warning message).
For example, the function F1 does not declare the exception specification, such a function can throw any kind of exception:
extern void F1 (); Can throw an arbitrary exception

Suppose there is a function F2 declare an exception of type int by its exception specification:
void F2 () throw (int);

F2 calling F1 is very legal, even though F1 may throw an exception that violates the F2 exception specification:
void F2 () throw (int) {  ...  F1 ();                  This is also valid even if the F1 may throw a                         //exception that is not of type int.  ...}

This flexibility is important when new code with exception specifications is integrated with old code with no exception specifications.
Because your compiler allows you to invoke a function that throws an exception that is inconsistent with the exception specification of the function that called it, and such a call may cause your program execution to be terminated, you should take steps to minimize this inconsistency when writing software. a good approach is to avoid using exception specifications within a template with type parameters. For example, the following template does not seem to throw any exceptions:
A poorly designed template WRT exception Specificationstemplate<class T>bool operator== (const t& LHS, const T & RHS) throw () {  return &LHS = = &RHS;}

This template defines an operator function operator== for all types. For any pair of objects of the same type, the function returns True if the object has the same address, otherwise false is returned.
This template contains exception specifications that indicate that a template-generated function cannot throw an exception. However, this may not be the case because the opertor& (address operator, see effective C + + clause 45) can be overloaded by some type of object. If overloaded,,opertor& may throw an exception when calling opertor& from within the operator== function, which violates our exception specifications, allowing program control to jump to unexpected.
The above example is a special case of a more general problem, the more general problem is that there is no way to know what kind of exception a template type parameter throws. It is almost impossible for us to provide a meaningful exception specification for a template. Because the template always uses the type parameter in different ways. The workaround can only be the template and exception specifications do not mix.
The ability to avoid calling the unexpected function The second method is to remove the exception specification for this function if you call other functions within a function that do not have an exception specification . This is easy to understand, but it is easy to ignore in practice. For example, allow the user to register a callback function:

A window System callback function pointer//When a window System event occurs, typedef void (*CALLBACKPTR) (int eventxlocation,                            int eventylocation,                            void *datatopassback);//window system class, containing a callback function pointer,//The callback function can be registered by Window System client class CallBack {public:  CallBack (callbackptr Fptr, void *datatopassback)  : Func (fptr), data (Datatopassback) {}  void makecallback (int eventxlocation,                    int eventylocation) const throw ();p rivate:  callbackptr func;               function to call when                                  //callback is made   void *data;                    Data to pass to callback};                                function//in order to implement the callback function, we call the registration function,//event's superscript and registration data as function parameters. void Callback::makecallback (int eventxlocation,                            int eventylocation) const throw () {  func (eventxlocation, Eventylocation, data);}

Calling Func here in Makecallback takes the risk of violating the exception specification because there is no way to know what type of exception the func throws.
Solve the problem by using more stringent exception specifications in the CALLBACKPTR typedef of the program:

typedef void (*CALLBACKPTR) (int eventxlocation,                            int eventylocation,                            void *datatopassback) throw ();

After defining the TypeDef, it would be illegal to register a callback function that might throw an exception:
A callback function with no exception specification void callBackFcn1 (int eventxlocation, int eventylocation,                  void *datatopassback); void * callBackData, ..... CallBack C1 (CALLBACKFCN1, callbackdata);                               Error! CALLBACKFCN1 may                               //throws an exception//with exception specification of the callback function void callBackFcn2 (int eventxlocation,                  int eventylocation,                  void * Datatopassback) throw (); CallBack C2 (CALLBACKFCN2, callbackdata);                               Correct, callBackFcn2                               //No exception specifications

This exception specification check when passing a function pointer is a newer feature of the language, so it is possible that your compiler does not support this feature. If they don't, then rely on yourself to make sure you can't make that mistake.
The third way to avoid calling unexpected is to handle exceptions thrown by the system itself. The most common of these exceptions is Bad_alloc, which is thrown by operator new and operator new[] When memory allocation fails (see clause M8). If you use the new operator in a function (also see clause M8), you must be prepared for a function that might encounter a Bad_alloc exception.
It is now often said that prevention is better than cure (ie: do everything in a rainy-day translator's note), but sometimes it is difficult to prevent and easy to treat. This means that sometimes it is easier to handle unexpected exceptions directly than to prevent them from being thrown. For example, you are writing a software that uses exception specifications precisely, but you must call the function from a library that does not use exception specifications, and it is impractical to prevent the unexpected exception from being thrown because it requires changing the code in the library.
While it is impractical to prevent the unexpected exception from being thrown, C + + allows you to replace the unexpected exception with a different exception type, which you can take advantage of. For example, you want all unexpected exceptions to be replaced with unexpectedexception objects. You can write code like this:
Class Unexpectedexception {};          All unexpected exception objects are                                       //replaced with this type of object void convertunexpected ()               //If a unexpected exception is thrown by {                                      //, this function is called  Throw Unexpectedexception ();  }

Let the above code run by replacing the default unexpected function with the convertunexpected function. :
set_unexpected (convertunexpected);

When you do this, a unexpected exception will trigger the call to the convertunexpected function. The unexpected exception is replaced by a unexpectedexception new exception type. If the exception specification that is violated contains a unexpectedexception exception, then the exception pass will continue as if the exception specification is always met. (If the exception specification does not contain unexpectedexception,terminate will be called, as if you did not replace unexpected)
Another way to turn a unexpected anomaly into a well-known type is to replace the unexpected function and let it re-throw the current exception, so that the exception is replaced with Bad_exception. You can write this:

void convertunexpected ()          //If an unexpected exception is thrown by {                                 //, this function is called  throw;                          It just re-throws the current}                                 //Exception set_unexpected (convertunexpected);                                  Install convertunexpected                                  //As a substitute for unexpected//                                  

If you do this, you should include bad_exception (or its base class, Standard class exception) in all exception specifications. You will no longer have to worry if encountering a unexpected exception will cause the program to terminate. Any disobedient exception will be replaced with Bad_exception, which continues to be passed in place of the original exception.
By now you should understand that exception specifications can cause a lot of trouble. The compiler can only partially detect that their use is consistent, using them in a template is problematic, it is easy to violate them without noticing them, and when they are violated by default, they can cause the program to stop running. Another disadvantage of exception specifications is that they can cause unexpected to be triggered, even if a high-level caller is prepared to handle the thrown exception, such as the following almost a word from the clause M11 example:
Class Session {                  //For modeling onlinepublic:                          //Sessions  ~session ();  ... private:  static void Logdestruction (Session *objaddr) throw ();}; Session::~session () {  try {    logdestruction (this);  }  catch (...) {  }}

The session destructor call Logdestruction records information about the session object being freed, and it explicitly captures all exceptions thrown from logdestruction. However, Logdestruction's exception specification indicates that it does not throw any exceptions. Now assume that the function called by Logdestruction throws an exception, and logdestruction is not captured. We don't expect such a thing to happen, but as we can see, it's easy to write code that violates the specification of the exception. When this exception is passed through Logdestruction, unexpected will be called, which will cause the program to terminate execution by default. This is the right behavior, but is this the behavior that the author of the session destructor wants? The author wants to handle all possible exceptions, so it seems that the program should not be terminated without the opportunity to execute the CATCH block in the session destructor. This does not happen if the logdestruction does not have an exception specification (a way to prevent this is to replace unexpected as described above).
It is very important to look at the anomaly specifications in a comprehensive perspective. They provide excellent documentation of the kind of exception that a function throws, and in the case of violating it, there are dire consequences, the program is immediately terminated, and they do so by default. At the same time, the compiler will only partially detect their consistency, so they can easily be violated in a casual way. And they will prevent the high-level exception handler from handling unexpected exceptions, even if the exception handlers know how to do it.
In summary, exception specifications are a feature that should be used with caution. Before adding them to your function, you should consider whether the behavior they bring is what you want.



More effective C + +----(14) prudent use of exception specifications (Exception specifications)

Related Article

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.