More Effective C ++ Terms 14

zhaozj2021-02-08  241

Terms 14: Exception Specifications (Exception Specifications)

There is no doubt that the abnormal specification is a compelling characteristic. It makes the code easier to understand because it explicitly describes what exception can be thrown. But it is not just a fun comment. The compiler can sometimes detect inconsistencies in an abnormal specification when compiling. And if a function throws an exception in the range of abnormal specifications, the system can detect this error while running, and then a special function UNEXPECTED will be automatically called. The abnormal specification can be used as a guided document and is also an abnormally used forced constraint mechanism. It seems to have a very attractive appearance.

However, in general, beautiful appearance is just a layer of skin, the beauty of the appearance does not represent its inner quality. Function UNEXPECTED acknowledgment is called function Terminate, and Terminate default behavior is to call function Abort, so a procedure for violating exception specifications is HALT (stop running). The local variables in the activated STACK FRAME are not released because Abort does not make such a clear operation when closing the program. Violation of an abnormal specifications becomes a disaster that should not occur.

Unfortunately, we can easily prepare a function that caused this disaster. The compiler only partially detects whether an abnormal use is consistent with an exception specification. A function calls another function, and the latter may throw an exception to violate the former exception, (A function calls the B function, because the B function may throw an exception within the A function exception, so this function call Extordual Specifications Translators in violation of A-function) The compiler does not detect this situation, and language standards also prohibit them to reject such calls (although warning information can be displayed).

For example, the function F1 does not declares an exception specification, such a function can throw any kind of exception:

Extern void f1 (); // can throw any exception

Suppose there is a function F2 to declare its exception to the INT type through its exception specification:

Void f2 () throw (int);

F2 call f1 is very legal, even if F1 may throw an exception to violate the F2 exception specification:

Void f2 () throw (int)

{

...

F1 (); // Even if F1 may throw out is not int type

// Abnormally, this is also legal.

...

}

This flexibility is important when the new code with an abnormal specification is integrated with the old code without an abnormal specification.

Because your compiler allows you to call an exception of the exception that is thrown, it is inconsistent with the exception specification of the function of the call. Small to least. A good way is to avoid using an exceptional specifications within a template with type parameters. For example, the following template does not seem to throw any exception:

// a poorly designed Template WRT Exception Specifications

Template

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 type, if the object has the same address, the function returns true, otherwise returns false.

This template contains an exception specification indicates that the function generated by the template cannot throw an exception. But the facts may not be like this, because Opertor & (Address Operators, see Effective C Terms 45) can be overloaded by some types of objects. If it is overloaded, when calling Operator & Operator & when calling from the operator == function, Opertor & Must throws an exception, which violates our exceptional specifications so that the program control jumps to UNEXPECTED. The above example is a general case of a more general problem, and this problem is that there is no way to know what kind of exception is thrown with a template type parameter. We can hardly provide a meaningful exceptional specification for a template. Since the template always uses different methods to use type parameters. Solution can only be a template and an abnormal specification not to use.

The second method that avoids calling the UNEXPECTED function is if there is no other function that does not have an exception-specific function, and the exception specification of this function should be removed. This is easy to understand, but it is easy to neglect in practice. For example, allow users 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, contains a callback function pointer,

// This backup function can be registered by the WINDOW system customer

Class callback {

PUBLIC:

Callback (CallbackPtr FPTR, VOID * DATATOPACK)

: Func (FPTR), Data (DataTopassBack) {}

Void makecallback (int eventXLocation,

INT EVENTYLOCATION) Const throw ();

Private:

Callbackptr func; // function to call when

// Callback is Made

Void * data; // data to pass to callaback

}; // function

/ / In order to realize the callback function, we call the registration function,

// The work of the event is functional parameters with the registration data.

Void Callback :: Makecallback (int eventXLocation,

Int evenTylocation) const throw ()

{

Func (EventXLocation, EventYLocation, DATA);

}

Here, FUNC is called in Makecallback, and it is necessary to risk the risk of exception specifications, because Func will not know what kind of exception will be thrown.

Solve the problem by using a stricter exception specification in the program in CallbackPtr Typedef:

TypeDef void (* CallbackPtr) (int eventXLocation,

INT EventYLocation,

Void * datatopassback).

After defining the typedef, if you register a callback function that may throw an exception will be illegal:

// A callback function without abnormalities

Void Callbackfcn1 (int eventxLocation, int eventylocation,

Void * DataTopassback;

Void * CallbackData;

...

Callback C1 (Callbackfcn1, CallbackData);

//error! Callbackfcn1

// Throw an exception

// Tonance function void callbackfcn2 with an abnormal specification (Int EventXLocation,

INT EventYLocation,

Void * datatopassback).

Callback C2 (Callbackfcn2, CallbackData);

// Correct, CallbackFCN2

// No abnormal specification

This exception specification is performed when the function pointer is transferred, which is a newer characteristic of the language, so it is possible that your compiler does not support this feature. If they don't support, then you can do it yourself to make sure you can't make this mistake.

Avoiding the third way to call UNEXPECTED is to process the exception thrown by the system itself. These exceptions are Bad_alloc, which is thrown when the memory allocation fails (see Terms 8). If you use the New operator in the function (see Terms 8), you must prepare the function for the function of Bad_alloc.

It is often said that it is often better than the treatment (that is, there is anything else to pay for it), but sometimes it is difficult to prevent difficulties. That is to say, sometimes the unExpected exception is easier than the prevention of them from being thrown. For example, you are writing a software, using an exceptional specification, but you must call a function from the library that does not use an exception specification, to prevent the unrealtened unmexpected exception, as this requires changing the code in the library.

Although it is unrealistic to prevent unmexpected exceptions, C allows you to replace Unexpected anomalies with other different exceptions, you can use this feature. 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

/ / Replace this type of object

Void ConvertuneXpected () // If a UNEXPECTED is abnormal

{// throw, this function is called

Throw unnexpectedexception ();

}

By replacing the default UNEXPECTED function with the CONVERTUNEXPECTED function, the above code begins to run. :

Set_unexpected;

When you do this, a UNEXPECTED exception will trigger the CONVERTUNEXPECTED function. Unexpected exception is replaced by a new exception type of UNEXPECTEDEXCEPTION. If the unusual content of the violated contains unmexpectedException, so abnormal transmission will continue, as if the exception is always satisfied. (If the exception specification does not contain unExpectedException, Terminate will be called, it is as if you have not replaced unExpected)

Another way to convert UNEXPECTED abnormalities into well-known types is replacing the Unexpected function, allowing it to reap out the current exception, so exception will be replaced with Bad_Exception. You can write like this:

Void ConvertuneXpected () // If a UNEXPECTED is abnormal

{// throw, this function is called

THROW; // It only re-throws the current

} // abnormal

Set_unexpected;

// Install ConvertuneXpected

// is Unexpected

// replacement of

If you do this, you should include Bad_Exception (or its base class, standard eXception) in all exception specifications. You will don't have to worry again if you encounter UNEXPECTED anomalies will cause the program to run. Any unused exception will be replaced with Bad_Exception, which is replaced by the original exception.

To now you should understand the abnormal rules can lead to a lot of trouble. The compiler can only partially detect whether their use is consistent, and they use them in the template. If they don't pay attention to them, they are easily violated, and they will be violated when they are violated in the default. An exceptional specifications have a disadvantage that they can cause UNEXPECTED to trigger even if a high-level caller is ready to process an exception thrown, such as this almost one word is from the terms 11 examples:

Class session {// for modeling onlineLine

Public: // sessions

~ Session ();

...

Private:

Static void logdestruction (session * objaddr) throw ();

}

Session :: ~ session ()

{

Try {

LogDestruction (this);

}

Catch (...) {}

}

The session destructive function calls the LogDestruction record. Information about the SESSION object is released, which clearly wants to capture all anomalies thrown from the logDestruction. However, the abnormal specifications of LogDestruction indicates that they do not throw any exceptions. Now suppose to throw an exception that the function called by the LogDestructionTRuction is thrown, and the LogDestruction is not captured. We will not expect such a thing. Anyone who is as we have seen is easy to write the code that violates the abnormal specifications. When this exception passes through the LogDestruction, UNEXPECTED will be called, and the program will be executed by the process. This is a correct behavior, is this the author's author hopiasis of the author of the session destructor? The author wants to handle all possible exceptions, so it seems that the program should not be terminated to the chance to execute in the SESSION session function. If the logDestruction is no abnormal specification, this kind of thing will not happen. (A prevention method is replaced as the UNPECTED as described above)

It is very important to look at an abnormal specification with a comprehensive perspective. They provide excellent documentation to illustrate a function that throws an exception, and in violation of it, there will be terrible results, the program is immediately terminated, and they will do this when default. At the same time, the compiler only partially detects their consistency, so they are easily violated inadvertently. And they will prevent the High-Level Exechood processor from handling Unexpected exception, even if these exception processors know how to do it. In summary, an abnormal specification is a knous that should be used prudent. Before adding them to your function, consider whether the behavior they bring is what you want.

转载请注明原文地址:https://www.9cbs.com/read-998.html

New Post(0)