Use destructors to avoid divulging resources

  • As long as you stick to this rule and encapsulate the resource in an object (like the smart pointer shared_ptr), you can usually avoid exposing the resource when exceptions occur.
  • To put it simply, when a resource may be unable to be released when the function throws an exception, then the resource can be encapsulated into an object (RAII), and the object’s destructor can be used to automatically release the resource, so that even if exceptions occur, there will be no resource leakage.

Prevent resource leaks in constructors

  • C++ only destructs objects that have been constructed. An object is not properly constructed until its constructor has executed.
  • Since C++ does not automatically clean up objects that “throw exceptions during construction,” you must design your constructors to clean themselves up in that case as well. Usually this is simply a matter of catching all possible exceptions, performing some sort of cleanup, and then rethrowing the Exception to propagate it further.
  • However, it would actually be more elegant to treat the objects that need to be initialized in this constructor as resources and leave them to be managed by smart Pointers.
  • The conclusion is: If you replace Point Class Members with an auto_ptr (using shared_ptr or unique_ptr after C++11) object, you’ve reinforced your constructors. It eliminates the “exceptions when resource leaks occur” crisis, eliminates the need to release resources in destructors, and allows const member Pointers to be handled in the same elegant way as non-const member Pointers.

Article 11: Exceptions are prohibited from leaving Destructors

Session::Session() { try { logDestruction(this); } catch (...) {}}
  • The catch block here looks like it doesn’t do anything, but it looks deceptive. This block prevents “exceptions thrown by LogDestruction” from going out of the Session Destructor.
  • There are two good reasons why we should “do our best to prevent exceptions from coming out of Destructors”. First, it avoids terminate being called in the stack-unwinding mechanism of the exception propagation. Second, it helps to ensure that the destructors do everything they are supposed to do.

Understand the difference between “throwing an Exception” and “passing an argument” or “calling a virtual function

  • Throwing an exception is similar to passing an argument in that they can be passed by value, by reference, or by pointer. However, depending on whether you’re passing in parameters or exceptions, things can happen quite differently. The reason is that when you call a function, control eventually returns to the calling end (unless the function fails so that it cannot return), but when you throw an Exception, control does not return to the throwing end.
  • C++ specifically states that when an object is thrown as an exception, a copy always occurs. Copying occurs even when a catch statement parameter is by reference, or when a thrown object is declared static. And the copy action is always based on the static type of the object.
  • The fact that “Exception objects are bound to cause replication behavior” also explains another difference between “passing parameters” and “throwing exceptions” : the latter is often slower than the former.
  • In general, you must use the following statements:

throw; To rethrow the current Exception, there is no opportunity to change the type of Exception propagated. In addition, it is more efficient because no new Exception Objects need to be generated.

  • Passing a temporary object to a non-const reference parameter during a function call is not allowed, but is legal for an exception.
  • The third difference between throwing Exception and passing an argument is that, in general, implicit conversions that are allowed during a call to a function passing an argument do not occur during the “exceptions match the catch clause” process.
  • During the process of “exceptions match the catch clause”, only two conversions can occur. The first is “class transformations in inheritance architectures”. The second is to change from a “typed pointer” to a “non-typed pointer” (so a catch clause with a const void* argument can catch exceptions of any pointer type).
  • Catch clauses always try to match in the order in which they appear. Compared to virtual calls, virtual functions use a “best fit” policy, while the Exception handling mechanism uses a “first fit” policy. Therefore, never put a “catch clause designed for a base class” before a “catch clause designed for a derived class”.

Catch exceptions by reference

  • In contrast to by reference, catching Exception in the by value manner results in two copies of the passed object being copied twice. One construct is for “the temporary object that will result from any exceptions,” and the other construct is for “copying the temporary object to the catch’s parameter.”
  • Never throw a pointer to a local object because the local object will be destroyed when the exception is passed from its scope, so the catch clause will get a pointer to the “destroyed object.”
  • If you catch by reference, you can avoid the problem of object deletion (which by pointer will) — it’s just too much for you to do, too much for you to do; You can also avoid the problem of cutting Exception objects (Exception objects of a derived class are captured and treated as objects of a base class, and will lose their derived components. The object cutting problem is caused by static concatenation and does not occur with references and Pointers); You can retain the ability to catch C++ standard exceptions; You also constrain the number of times the Exception Objects must be copied.

Use Exception Specifications Wisely

  • Exception specification example. A function that only throws Exceptions of type int is declared as:
void fun() throw(int);
  • If a function throws an Exception that is not included in the Exception specification, the error will be detected at run time and the special function unexpected will be called automatically. The default behavior for unexpected is to call terminate.
  • One way to avoid unexpected events is to:

    • Templates and Exception Specifications should not be mixed.
    • If function A calls function B and function B has no Exception specifications, then function A should not set the Exception specifications itself.
    • Handle exceptions that the “System” may throw (such as BAD_ALLOC).
  • C++ allows you to replace unexpected exceptions with different types of exceptions. If the replacement of the unexpected function rethrows the current Exception, it is replaced with the standard type BAD_EXCEPTION.
void convertUnexpected()

  • If you do this, and each Exception Specifications contains BAD_EXCEPTION, or its base class, you will never have to worry about the program aborting an unexpected Exception.

Understand the cost of exception handling

  • To minimize the cost associated with exceptions, the compiler does not support exceptions as long as it is possible to dissupport exceptions; Please limit your use of the try block and Exception specifications to those that are absolutely necessary, and throw exceptions only when there are true exceptions.