Prevent exception information from being passed to a destructor in C + +

Source: Internet
Author: User
Tags constructor terminates


A destructor is invoked in two cases. The first is to delete an object under normal circumstances, such as an object that is outside the scope or explicitly deleted. The second is an exception-passing stack-stack-unwinding process, in which an object is deleted by the exception-handling system.



In both cases, the exception may or may not be active when the destructor is invoked. Unfortunately there is no way to differentiate between these two situations within the destructor. So in writing destructors you must conservatively assume that exceptions are activated, because if an exception is activated and the destructor throws an exception, and the program control is transferred to the destructor, C + + calls the Terminate function. This function functions as its name indicates: it terminates the operation of your program, and terminates immediately, and even the local object is not released.



As an example, a session class used to track the sessions,session of an online computer is something that runs during this period of time from the moment you log on to the computer until you log out of the system. Each Session object is concerned with the date and time it was created and released:



class Session {
public:
 Session();
 ~Session();
 ...
private:
 static void logCreation(Session *objAddr);
 static void logDestruction(Session *objAddr);
};



function logcreation and logdestruction are used to record object creation and release respectively. We can thus write the destructor of the session:



Session::~Session()
{
 logDestruction(this);
}



Everything looks good, but what happens if logdestruction throws an exception? The exception is not captured by the session's destructor, so it is passed to the caller of the destructor. But if the invocation of the destructor itself originates from the throw of some other exception, then the Terminate function is automatically invoked to terminate your program completely. This is not what you want to happen. The program does not record the release of the object information, which is unfortunate, even a big trouble. So is the state of affairs really serious enough to terminate the operation of the program? If not, you must prevent exceptions thrown within the logdestruction from being passed outside of the session destructor. The only way is to use try and catch blocks. It's a natural way to write a function like this:



Session::~Session()
{
 try {
  logDestruction(this);
 }
 catch (...) {
  cerr << "Unable to log destruction of Session object "
   << "at address "
   << this
   << ".\n";
 }
}



But doing so is no more secure than your original code. If an exception is thrown when the operator<< is invoked in a catch, we encounter an old problem, and an exception is forwarded to the outside of the session destructor.



We can put a try in the catch, but there must be a limit, otherwise it will fall into the loop. So we have to ignore all the exceptions it throws when we release the session:



Session::~Session()
{
 try {
  logDestruction(this);
 }
 catch (...) { }
}



The catch surface seems to have done nothing, which is an illusion that in fact it prevents any exceptions thrown from logdestruction from being passed outside of the session destructor. We can rest easy now, no matter whether the session object is freed in stack unwinding, the Terminate function is not invoked.



There is a second reason why exceptions are not allowed to pass to destructors. If an exception is thrown by a destructor and is not caught inside the function, then the destructor does not run completely (it stops at the place where the exception is thrown). If the destructor does not run completely, it cannot complete everything that you want it to do. For example, we make a modification to the session class, which starts a database transaction (DB transaction) when it is set up, and ends the transaction when it terminates:



Session::Session() // For simplicity,
{// this constructor does not
/// Handling exceptions
LogCreation (this);
StartTransaction (); // start
database transaction
}
Session::~Session()
{
 logDestruction(this);
 endTransaction(); // 结束database transaction
}



If Logdestruction throws an exception here, the transaction that is started within the session constructor is not terminated. We might be able to eliminate the problem by readjusting the order of functions in the session destructor, but if Endtransaction throws an exception, we have no choice but to go back to using try and catch.



To sum up, we know that there are two reasons to prohibit exceptions from being passed to destructors, first being able to prevent terminate from being invoked in the process of stack-unwinding of the stack that is transmitted by the exception. Second, it helps to ensure that destructors always do everything we want it to do. (If you're still not convinced by the reasons I'm talking about, you can go to the Herb Sutter article Exception-safe Generic containers, especially "destructors" Throw and Why They ' re Evil " This paragraph).


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.