More Effective C ++ Terms 12

zhaozj2021-02-08  384

Terms 12: Understanding the difference between "throwing an exception" and "transmitting a parameter" or "call a virtual function"

From the grammatical point of view, declare the parameters in the function and declare the parameters in the Catch clause. There is almost no difference:

Class widget {...}; // a class, what kind of specifically

/ / Is not important here

Void F1 (Widget W); // Some functions, its parameters are

Void F2 (Widget & W); // 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

Catch (widget & w) ... // captures exceptions, the type of abnormality is

Catch (Const Widget & W) ... // Widget, Widget &, or

Catch (widget * pw) ... // widget *

Catch (const widget * pw) ...

You may therefore think that it is basically the same in the Catch clause with throw to the CATCH clause. There are some same points in this, but they also have a huge difference.

Let us talk from the same point. The way you transfer function parameters and abnormal path can be a pass value, pass reference, or transfer pointer, which is the same. However, when you pass parameters and anomalies, the operation process to be completed is completely different. The reason for this difference is that when you call the function, the program's control will eventually return to the call of the function, but when you throw an exception, the control will never return to the abnormality.

There is such a function, the parameter type is widget, and throws an exception of a Widget type:

// A function, read the value from the stream into the widget

iStream Operator >> (ISTREAM & S, Widget & W);

Void passandthrowWidget ()

{

Widget Localwidget;

CIN >> LocalWidget; // Transfer LocalWidget to Operator >>

Throw localwidget; // Throw LocalWidget exception

}

When passing the localwidget to the function operator >>, no copy operation, but to point the reference type variable W within Operator >>, any of the operations of W's actually applied to the LocalWidget. This is very different from the throwing LocalWidget exception. Regardless of whether the value is captured through the value or by reference capture (cannot capture this exception through the pointer, the copy operation of the LCALWIDGET will be performed by the type mismatch), and it is said that the copy to the CATCH clause is the copy of the localwidget. It must be done because the destructor will be called when the LocalWidget leaves the living space. If the LocalWidget itself is passed to the Catch clause, this clause is only a destructed Widget, a Widget's "body". This is not available. Therefore, the C specification requires that the object that is abnormally thrown must be copied.

Even if the object thrussed is not released, copy operation is also possible. For example, if the PassandThrowWidget function declares that localwidget is static variable (static), void passandthrowwidget ()

{

Static Widget LocalWidget; // is now a static variable (STIC);

// Contains the end of the program

CIN >> Localwidget; // 那 那

Throw localwidget; // will still be Localwidget

} // Copy the operation

A copy of the LocalWidget will be copied when throwing anomalies. This means that even if it captures exceptions by reference, it cannot modify the localwidget in the catch block; only the copy of the LocalWidget can be modified. Forced copy copies of the abnormal objects, this restriction helps us understand the second difference between parameters transfer and throwing abnormal: throwing an exception running speed is slower than parameters.

When an exception object is copied, the copy operation is done by the copy constructor of the object. The copy constructor is a copy constructor of the static type of the object, rather than the dynamic type of the object (Dynamic Type) copy constructor. For example, the following pass a little modified PassandthrowWidget:

Class widget {...};

Class SpecialWidget: Public Widget {...};

Void passandthrowWidget ()

{

SpecialWidget Localspecialwidget;

...

Widget & rw = localspecialwidget; // rw reference specialWidget

Throw rw; // It throws a type Widget

// Abnormally

}

The abnormal object throw here is a widget, even if the RW is referenced is a SpecialWidget. Because the static type of RW is Widget, not the SpecialWidget. Your compiler does not have the main to RW references to a SpecialWidget. The compiler is noted that the static type of the RW. This behavior may be different from what you expect, but this is consistent with the behavior of copy constructor in C in other cases. (However, there is a technology that allows you to copy according to the dynamic type Dynamic Type of the object, see Terms 25)

An exception is the copy of other objects, this fact implies how you throw an exception in the Catch block. For example, the two CATCH blocks below, you look like:

Catch (widget & w) // Captive Widget exception

{

... // Treatment exception

Throw; // Re-throw an exception, let it

} // Continue transmission

Catch (widget & w) // Captive Widget exception

{

... // Treatment exception

Throw w; // Passing is captured

} // Copying the difference between the two CATCH blocks is that the first CATCH block re-thrown the current captured exception, and the second Catch block re-thrown a new copy of the current capture exception. If you ignore system overhead generation of additional copies, is there a difference in these two methods?

Of course. The first block re-throws the current exception, no matter what type it is. Especially if this exception starts to be thrown as the SpecialWidget type, then the first block is transferred or the SpecialWidget exception, even if the W's static type is Widget. This is because no copy operation is made when re-throwing anomalies. The second Catch block re-throwing new exception, the type is always widget, because W's static type is Widget. In general, you should use

Throw

To reap out the current exception, because this will not change the type of abnormality being passed, and more efficient, because you don't have to generate a new copy.

(By the way, the abnormality generated copy is a temporary object. As explained in Terms 19, the temporary object can make the compiler optimize its survival period, but I think your compiler is hard to do. Because there are few abnormalities in the program, the compiler manufacturer will not spend a lot of energy in this regard.)

Let us test the following three Catch clauses used to capture widget exceptions, exceptions are thrown as the passandthrowwidgetp:

Catch (widget w) ... // capture exceptions through the value

Catch (widget & w) ... // Capture by transfer

// abnormal

Catch (Const Widget & W) ... // References to Const by passing

// Capture anomaly

We immediately noticed another difference between passing parameters and communication abnormalities. An object that is abnormal (just explained, always a temporary object) can be captured by ordinary references; it does not need to capture the reference-to-const) by reference to the reference object. In the function call, it is not allowed to deliver a temporary object to a parameter of a non-Const reference type (see Terms 19), but it is allowed in an abnormality.

Let us first return to the test of the abnormal object copy. We know the parameters of the function when using the pass value, we have created a copy of the object (see Effective C Terms 22) and store this copy to the parameter of the function. Similarly, when we pass an exception by passing the value, it is done. When we declare a Catch clause:

Catch (Widget W) ... // Capture

Two copies of the objects that have been thrown, one is a temporary object that must be established by all anomaly, and the second is to copy the temporary object into W. Similarly, when we capture anomalies by reference,

Catch (Widget & W) ... // Capture by reference

Catch (const widget & w) ... // also captured by reference

This still creates a copy of the object being thrown: Copy is a temporary object. Reflections When we pass the transfer function parameters, we do not have object copies. When an exception is thrown, the copy number of the system constructed (subsequently fractionated) is thrown to the copy number of the object as the same object to the function as a parameter to the function. We have not discussed abnormalities through pointers, but through pointers throwing exceptions are the same as passing parameters through pointers. No matter which method is a copy of a pointer being passed. The pointer you can't think of throw is a pointer to the local object because the local variable has been released when an exception exits the living space of the local variable. The CatCH clause will get a pointer to the object that has not been present. This behavior should be avoided when designing.

The object is passed from the functional call to the function parameter and the method used from the abnormal throwing point to the Catch clause. This is only one aspect of the difference between parameter delivery and abnormal transmission, the second difference is in the function call The process of improving the type matches the type between the anomalous and the modified or abnormal capture. For example, SQRT functions in the Standard Math Library: THE STANDARD MATH LIBRARY:

Double SQRT (Double); // from or

We can calculate the square root of an integer as follows:

INT I;

Double SQRTOFI = SQRT (i);

There is no doubt that C allows for implicit type conversion from int to double, so in the call of SQRT, i is quietly transformed into a Double type, and its return value is Double. (For detailed discussion on implicit type conversion, see Terms 5) In general, the Catch clause does not perform such a conversion when matching an abnormal type. See the code below:

Void f (int value)

{

Try {

IF (someFunction ()) {// If somefunction () returns

Throw value; // I really throw a shaped value

...

}

}

Catch (double d) {// only processes the exception of the Double type

...

}

...

}

The int exception thrown in the TRY block will not be processed by the Double exception Catch clause. This clause can only capture an exception that is really a double type; does not perform type conversion. So if you want to capture int abnormal, you must use the Catch clause with the INT or INT & parameters.

However, two types of conversion can be performed when an abnormality match is performed in the CatCH clause. The first is the conversion between inheritance and base classes. A Catch clause used to capture the base class can also handle the exception of the derived class type. For example, a Diagnostics Portion (see Effective C Terms 49), for example, the Diagnostics Portion (see Effective C Terms 49).

Capture the runtime_errors anomaly Catch clause to capture the orthomy of the RANGE_ERROR type and the Overflow_Error type, which can receive the root Exception exception Catch clause to capture any of its derived class exceptions.

This type of derivation and base class (inheritance_based) can act on values, references, and pointers:

Catch (runtime_error) ... // can catch errors of type

Catch (Runtime_ERROR &) ... // Runtime_ERROR,

Catch (const runtime_error &) ... // range_error, or // overflow_error

Catch (Runtime_ERROR *) ... // can catch errors of type

Catch (const runtime_error *) ... // runtime_error *,

// range_error *, or

// Overflow_ERROR *

The second is to allow transition from a Typed Pointer to a united Pointer, so a Catch clause with a Const Void * pointer can capture any type of pointer type:

Catch (const void *) ... // captures any pointer type unusual

The last difference between passing parameters and delivery abnormalities is that the CATCH clause matching order always depends on their order that appears in the program. Therefore, a derived class anomaly may be processed with a Catch clause capture of its base class anomaly, even if there is a CATCH clause that can process the derived class abnormality, correspond to the same TRY block. E.g:

Try {

...

}

Catch (logic_error & ex) {// This catch block will capture

... // All Logic_ERROR

} // Abnormal, including its derived class

Catch (invalid_argument & ex) {// This block will never be executed

... // Because of all

} // invalid_argument

// Abnormally be above

// Catch clause capture.

In contrast to the above behavior, when you call a virtual function, the called function is located in a class that is the dynamic type of the object that is called. You can say that the virtual function uses the optimal fit, and the abnormality processing is the first suitable method. If a Catch clause of a derived class exception is located in front of the Catch clause that processes the base class exception, the compiler will warn. (Because such code is usually illegal in C .) But you'd better do pre-prevention: Do not put the CATCH clause that processes the base class abnormally in front of the CATCH clause that deals the derived class exception. I should write this like this:

Try {

...

}

Catch (Invalid_argument & ex) {// Handling Invalid_argument

... // Abnormal

}

Catch (logic_error & ex) {// Handling all other

... // logic_errors unusual

}

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

New Post(0)