More effective C + +----(12) Understand the difference between "throw an exception" and "pass a parameter" or "call a virtual function"

Source: Internet
Author: User
Tags square root throw exception

Item M12: Understanding the difference between "throw an exception" and "pass a parameter" or "call a virtual function"
Syntactically, there is little difference between declaring a parameter in a function and declaring a parameter in a catch clause:

Class Widget {...};                 A class, specifically what class                                 //Here is not important void F1 (Widget W);                    Some functions, whose parameters are void F2 (widget& w) respectively;                   Widget, widget&, or void F3 (const widget& w);             widget* type void F4 (Widget *pw); void F5 (const widget *PW); catch (widget W) ...                  Some catch clauses, used to catch (widget& W) ...               Catch exception, type of exception is catch (const widget& W)           ... Widget, Widget&, or catch (widget *PW) ...                

You might therefore think that throwing a throw exception into a catch clause is basically the same as passing a parameter through a function call. There are some similarities, but there are huge differences.
Let's start with the same point.The way you pass a function argument to an exception can be a pass-through , a reference, or a pass pointer, which is the same. but when you pass parameters and exceptions, the process of doing this is completely different. The reason for this difference is that when you invoke a function, the control of the program is eventually returned to the function's call, but when you throw an exception, control never returns to the place where the exception was thrown.
There is such a function, the parameter type is widget, and throws a widget-type exception:

A function that reads values from the stream into the widget IStream operator>> (istream& S, widget& W); void Passandthrowwidget () {  widget Localwidget;  Cin >> Localwidget;          Pass Localwidget to operator>>  throw localwidget;           Throw Localwidget Exception}

When passing localwidget into the function operator>>, No copy operation ( because the parameter is a reference ) , instead of operator>> the reference type variable W in the Localwidget, any operation on W is actually applied to the localwidget. This is quite different from throwing localwidget exceptions. whether you catch an exception by passing a value or catch it by reference (you cannot catch the exception by pointer, because the type does not match) the copy operation is Lcalwidget, and it is said that the copy of Localwidget is passed to the catch clause. this must be done because the destructor will be called when Localwidget leaves the living space. If the localwidget itself (rather than its copy) is passed to the catch clause, the clause receives only a refactored widget, a widget's "corpse". This is not available. Therefore, the C + + specification requires that the object being thrown as an exception must be copied. ( if the catch is a normal parameter, copy two times, or copy once if the catch is a reference parameter, but what if the throw itself is a reference? Do you copy it? Thinking: Localwidget is a local variable that is thrown out and released, so what's the point of throwing a reference? )
The copy operation occurs even if the object being thrown is not freed. For example, if the Passandthrowwidget function declares localwidget as a static variable (static),
void Passandthrowwidget () {  static Widget localwidget;        is now a static variable (statics);                                    Always exists until the end of the program  CIN >> Localwidget;               Run the  throw localwidget as before;                Copy operation will still be made to Localwidget}          

a copy of Localwidget will still be copied when an exception is thrown. This means that even if you catch an exception by reference, you cannot modify the Localwidget in the catch block; only the copy of Localwidget can be modified. Forcing a copy of the exception object , this restriction helps us to understand the second difference between parameter passing and throwing exceptions: throwing exceptions runs slower than parameter passing.
When the exception object is copied, the copy operation is done by the object's copy constructor. The copy constructor is a copy constructor of the class that corresponds to the static type of the object, rather than the copy constructor of the object's dynamic type, which corresponds to the class. For example the following is a slightly modified passandthrowwidget:
Class Widget {...}; Class Specialwidget:public Widget {...}; void Passandthrowwidget () {  specialwidget localspecialwidget;  ...  widget& rw = localspecialwidget;      RW references specialwidget  throw RW;                             It throws a type widget                                        //exception}

The exception object thrown here is the widget, even if RW refers to a specialwidget. because the static type of RW is a widget, not a specialwidget. Your compiler simply doesn't notice that RW is referencing a specialwidget. what the compiler is noticing is the static type of RW (static type). ). This behavior may not be the same as what you expect, but this is consistent with the behavior of copying constructors in C + + in other cases. (However, there is a technique that allows you to copy the dynamic type of the object, see clause M25)
An exception is a copy of another object that affects how you throw an exception in the catch block. such as the following two catch block, at first glance looks like:
catch (widget& W)                 //Catch Widget exception {  ...                             Handling exception  throw;                          Re-throw the exception, let it}                                 //Continue passing catch (widget& W)                 //Catch Widget exception {  ...                             Handling exception  throw W;                        Pass a copy of the caught exception}                    

The difference between these two catch blocks is thatThe first catch block re-throws the currently caught exception, and the second catch block re-throws a new copy of the currently caught exception. If you ignore the overhead of generating extra copies, are there differences between the two methods?
Of course. The first chunk is re-thrown in the current exception, no matter what type it is. In particular, if the exception starts out as a specialwidget type, then the Specialwidget exception is passed in the first block, even if the static type of W is a widget. This is the copy operation was not performed because the exception was re-thrown. The two catch blocks are re-thrown as new exceptions,The type is always a widget, because the static type of W is a widget.In general, you should use throw to re-throw the current exception, because it does not change the type of exception that is passed out, and is more efficient because you do not need to generate a new copy. Incidentally, the copy generated by the exception is a temporary object. As explained in clause 19, the temporary object allows the compiler to optimize its lifetime (optimize it out of existence), but I think your compiler is hard to do this because there are few exceptions in the program, so the compiler vendor does not spend a lot of effort on this. )
Let's test the following three catch clauses that are used to catch widget exceptions, which are thrown as PASSANDTHROWWIDGETP:
catch (Widget W)                ... Catch exception catch by passing value (widget& W)               ... Catch                                //exception catch by passing a reference (const widget& W)         ... By passing a reference                               //catch exception pointing to a const

We immediately noticed another difference between passing parameters and passing exceptions. An object thrown by an exception (as explained earlier, always a temporary object) can be captured by a generic reference, and it does not need to be captured by a reference to a const object (reference-to-const). forwarding a temporary object to a parameter of a non-const reference type (see clause M19) is not allowed in a function call, but is allowed in the exception.
Let's go back to the test of the exception object copy, regardless of the difference. we know that when passing the parameters of a function in a pass-through, we create a copy of the passed object (see effective C + + clause 22) and store the copy in the parameters of the function. The same is true when we pass an exception by passing a value. When we declare a catch clause like this:
catch (Widget W) ...//pass-through value capture

Will establish two xA copy of the object being thrown, One is a temporary object that all exceptions must have, and the second is to copy the temporary object into the W(Wq raise, Important: is two!) )。 Similarly, when we catch an exception by reference,

catch (widget& W)               ... Catch catch by reference (const widget& W) ...         Also captured by reference

This will still create aCopy of the object being thrown: the copy is also a temporary object. Instead, when we pass a function argument by reference, there is no object copy. when an exception is thrown, the number of copies that the system constructs (which will be destroyed later) is more than the number of copies that are constructed when the same object is passed as a parameter to the function. (What do you mean?) )
We have not yet discussed the case of throwing exceptions by pointers. But throwing an exception through a pointer is the same as passing a parameter through a pointer. Either way, a copy of the pointer is passed. However, you cannot assume that the thrown pointer is a pointer to a local object because the local variable was freed when the exception left the local variable's living space. The catch clause obtains a pointer to an object that does not already exist. This behavior should be avoided at design time. (Wq raises, that is to say: must be global or in the heap.
The object is passed from the call of the function to the function parameter in the same way that it is passed from the exception throw point to the catch clause, which is only one aspect of the difference between the parameter passing and the exception passing .The second difference is the process of matching the type between the caller of the function or the person who throws the exception and the callee or exception trap. For example, in the standard math library, the SQRT function:

Double sqrt (double);                 From <cmath> or <math.h>

We can do this by calculating the square root of an integer, as follows:

int i; Double Sqrtofi = sqrt (i);

No doubt C + + allows implicit type conversions from int to double,So in the SQRT call, I is quietly converted to a double type, and its return value is also double. (For a detailed discussion of about implicit type conversions See clause M5)In general, the catch clause does not perform such conversions when it matches the exception type. (type check is more stringent!!!) )See the following code:

void f (int value) {  try {    if (someFunction ()) {      ///If SomeFunction () returns      throw value;             True, throw an shaping value    ...    }  }  catch (double D) {           //Only handle exceptions of type double    ...    }  ...}

An int exception thrown in a try block is not caught by a catch clause that handles a double exception. The clause can only catch exceptions where the type really is a double, not a type cast. Therefore, if you want to catch an int exception, you must use a catch clause with an int or int& parameter.
However, there are two types of conversions that can be made when an exception is matched in a catch clause. The first is the transformation between the inheriting class and the base class. A catch clause used to capture a base class can also handle exceptions of the derived class type. For example, the diagnostic section in the standard C + + library (STL)-Defined exception class hierarchy (diagnostics portion) (see effective C + + clause 49).
Catch clauses that capture runtime_errors exceptions can capture exceptions of type Range_error and Overflow_error type ( where Range_error and Overflow_error are sub-classes of Runtime_error); catch clauses that can receive root class exception exceptions can catch any of their derived class exceptions.
The exception type conversion between this derived class and the base class (inheritance_based) can be used for numeric values, references, and pointers:

catch (Runtime_error)               ... Can catch errors of Typecatch (runtime_error&)              ... Runtime_error,catch (const runtime_error&)        ... Range_error, or                                 //Overflow_errorcatch (runtime_error*)              ... Can catch errors of Typecatch (const runtime_error*)        ... runtime_error*,                                        //range_error*, or//                                        overflow_error*

The second is to allow the conversion from a typed pointer (typed pointer) to an untyped pointer (untyped pointer), so a catch clause with a const void* pointer captures any type of pointer-type exception:

The last difference between passing parameters and passing exceptions is that the catch clause match order always depends on the order in which they appear in the program. therefore, a derived class exception may be caught by a catch clause that handles its base class exception, even if there is a catch clause that can handle the exception of the derived class directly, as opposed to the same try block. For example:

try {  ...} catch (logic_error& ex) {              //This catch block will capture  ...                                  All Logic_error}                                      //exceptions, including its derived class catch (invalid_argument& ex) {//         this block will never be executed ...                                  Because all}                                      //Invalid_argument                                       //Exceptions are captured by the above                                       //catch clause.

In contrast to the above behavior, when you invoke a virtual function, the called function is in the class closest to the dynamic type of the object that emitted the function call. You can say that. The virtual function uses the best fit method, and the exception processing adopts the first fit method. if a catch clause handling a derived class exception is located after the catch clause that handles the base class exception, the compiler issues a warning. (because such code is usually not legal in C + +.) But you'd better be proactive: do not put the catch clause that handles the base class exception in front of the catch clause that handles the derived class exception. Like the example above, it should be written like this:

try {  ...} catch (invalid_argument& ex) {          //handle invalid_argument  ...                                   Exception}catch (logic_error& ex) {               //handle all other  ...                                   Logic_errors exception}

In summary, there are three main differences between passing an object to a function or an object calling a virtual function and throwing an object as an exception. first, the exception object is always copied when it is passed, and the exception object is copied two times when it is captured by means of a pass value. Objects do not necessarily need to be copied when they are passed to a function as arguments. second, the object is thrown as an exception and passed as a parameter to the function, the former type conversion is less than the latter (the former has only two forms of conversion). Finally, the order in which the catch clauses match the exception types is the order in which they appear in the source code, and the first type matches the successful catch that will be used to execute. When an object invokes a virtual function, the selected function is in the class that matches the object type best, even if the class is not at the forefront of the source code.

More effective C + +----(12) Understand the difference between "throw an exception" and "pass a parameter" or "call a virtual function"

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.