More Effective C ++ Terms 11

zhaozj2021-02-08  248

Terms 11: Prohibiting Exceptions (Exceptions) is passed to the destructor

The destructor will be called in both cases. The first is to delete an object under normal circumstances, such as an object beyond the scope or explicitly delete. The second is that an abnormal processing system deletes an object during the stack-unwinding of abnormal transmission.

In both cases, the abnormality may not be in an active state when calling the destructuring function. Unfortunately there is no way to distinguish these two situations within the descent function. So when you write a function function, you have to be conservatively assumed to be activated because if you are activated, the destructive function also throws an exception and causes the program control to transfer to the destructor, C will call Terminate function. The role of this function is as indicated by its name: it terminates the run of your program, and it is terminated immediately, and even the local object is not released.

Let's take an example, a session class to track the sessions of online computers, and session is running from a login computer that starts to the time until you cancel the system until you cancel the system. Each Session object is concerned about its date and time it is established and released:

Class session {

PUBLIC:

Session ();

~ Session ();

...

Private:

Static void logcreation (session * objaddr);

Static void logdestruction (session * objaddr);

}

Functions LogCreation and LogDestruction are used to record the establishment and release of objects. We can therefore write the session destructor:

Session :: ~ session ()

{

LogDestruction (this);

}

Everything looks very good, but what will happen if LogDestruction throws an exception? The exception is not captured by the session destructor, so it is passed to the caller of the destructor. However, if the call of the patterned function itself is derived from some other abnormal throws, the Terminate function will be automatically called, completely terminating your program. This is not what you want to happen. The program does not record the information of the release of the object, which is unfortunate, even a big trouble. So do you really have a step where you must terminate the program? If not, you must prevent an exception thrown in the logDestruction to pass to the outside of the Session description function. The only way is to use TRY and CATCH Blocks. A natural practice will write functions like this:

Session :: ~ session ()

{

Try {

LogDestruction (this);

}

Catch (...) {

CERR << "Unable to log destruction of session object"

<< "at address"

<< this

<< "" ./n ";

}

}

But doing this is not safe than your original code. If an Operator << is called in Catch, it has been thrown out, and we have encountered the old problem, an exception being forwarded to the outside of the session destructor.

We can put TRY in Catch, but this has always had a limit, otherwise it will fall into the loop. So we must ignore all the exceptions throwing when you release the session:

Session :: ~ session ()

{

Try {

LogDestruction (this);

}

Catch (...) {}

}

There is no matter if you don't do anything on the surface. This is an illusion that it prevents any exceptions thrown from the LogDestruction to the SESSION session description function. We can now have no worries, regardless of the session object is released in the stack unwinding, the Terminate function will not be called. There is also a second reason for unusually transmitted abnormal transmission to the destructive function. If an exception is thrown without caught in the function, the destructor will not run completely (it will stop in that place to throw an exception). If the destructor is not running completely, it can't complete all the things you want to do. For example, we make a modification for the Session class, starting a database transaction (Database Transaction) when establishing the session, ending this transaction when ending the session:

Session :: session () // For simplicity,

{// This constructor is not

// Treatment exception

Logcreation (this);

StartTransAction (); // Start Database Transaction

}

Session :: ~ session ()

{

LogDestruction (this);

EndTransAction (); // End Database Transaction

}

If the LogDestruction is thrown out, it is not terminated in the Transaction in the session constructor. We may be able to eliminate problems by re-adjusting the function call order in the session destructor, but if endtransaction throws an exception, we have no choice but to returns to use TRY and CATCH.

In summary, we know that there are two reasons for prohibiting abnormal transmission to the destructuring function, and the first can prevent Terminate from being called during the stack-unwinding of the stack-unwinding. Second it helps ensure that the destructor can always complete all the things we hope to do. (If you still don't convince the reason I said, you can go see Herb Sutter's article Exception-Safe Generic Containers, especially the DESTRUCTORS THROW AND why're eviL ").

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

New Post(0)