Abnormalities in C and C ++ 16

zhaozj2021-02-16  51

Guidelines

According to the readers' suggestions, after reflection, I partially fix the principles of the declaration in Part 14:

l As long as it is possible, use those structural functions that do not leave their usual base classes and members.

l Don't throw any exceptions from your constructor.

This time, I will think about the reader's opinion, the wisdom of C prophet, and my own new understanding and improvement. They are then converted to the guidelines to clarify and attract those initial principles.

(Keyword Description: I use "child object" or "enclosed object" to represent an array element, unknown base class, a famous data member; use "inclusive object" to represent array, derived class objects, or data members Object.)

1.1 C essence

You may think that the constructor has a responsibility to throw a role in the event of an incorrect, to properly block the construction behavior of the inclusive object. HERB SUTTER wrote in a private letter:

An object's lifetime begins with the construction completion.

Introduction: An object does not exist when it is not completed.

Introduction: The only way to inflow structures failure is to exit the constructor with an exception.

I estimate that you are doing this concept ("wrong" because it does not conform to C essence), and this is the reason for doing difficulties.

"C essence" is a C myth that is mainly taught. It is our initial rule, from the ISO standard and the actual axiom. If there is no Bible that there is such a C essence, the chaos will rule the world. Given That No Actual Canon for the Spirit EXISTS, Confusion REIGNS over what is and is not with the package.

One of C and C is one of the essence of "Trust The Programmer". Like I write to Herb:

In the end, my "perfect" point is that the abnormal mapping of the abnormality in the wrong spread should be selected by the system designer. Don't always be best, but should do this. C is the most weaker place is that you can deviate from the preferred method you actually need. There are also other dangerous behaviors that are licensed by language, depending on whether you know what you are doing. In the end, my "perfect" objective was to map exceptions to some other form of error propagation should a designer choose to do so. Not that it was always best to do so, but that it could be done. One of the simultaneous strengths / Weakness Of C Is That You Can Deally NEED PREFERRED PATH INER DANGERES, Under The Assumption you know what you are doing.

C standards often tolerate and even license potentially unsafe behaviors, but not on this issue. Obviously, the judgment of the identity programmer should be obesed in a higher level (Apparently, The Desire to Allow Programmer Discretion Yields to a higher purple). Herb discovered this higher-level purpose on the second performance form of C essence: an object is not a real object (so unavailable) unless it is completely constructed (meaning that all of its elements are also It is fully constructed). Look at this example:

Struct X

{

A a a;

B B;

C C;

Void f ();

}

Try

{

X x;

x.f ();

}

Catch (...)

{

}

Here, A, B, and C are other classes. Suppose X.a and X.b constructs are completed, and an exception is thrown during the construction of X.c. As we see in the previous sections, the language rules set out such a sequence:

L X's constructor throws an abnormal

L X.B destructor is called

L X.A destructor is called

l Control power to the abnormal processing function

This rule complies with the essence of C . Because X.C did not complete the structure, it never became an object. Thus, X has never become an object because it has an internal member (X.C) never exists. Because there is no object really exists, there is no need to formally analyze.

Now, it is assumed that X's constructor does not know how to control the original exception. In this case, the execution sequence will be:

l x.f () is called

L X.C destructor is called

L X.B destructor is called

L X.A destructor is called

L X's destructor is called

l Control Right Skip Exception Processing Function Down

Those will then allow sectors that are never completely constructed (X.C and X). This will cause contradictions: a subject of death has never been produced. Through the forced constructive function, the language structure avoids this contradiction.

1.2 C ghost

The front shows that an object is really existed when it is completely constructed. But really an object is equivalent to being completely constructed? Especially X.C's construction failure "always" is so bad to die in really before being generated?

Before the C language is abnormal, the definition process of X must be successful, and the call to X.f () will be executed. Instead of the foreurcation method, we will call a status detection function:

X x;

IF (x.is_ok ())

x.f ();

Or use a backhaul status parameter:

BOOL IS_OK;

X x (is_ok);

IF (is_ok)

x.f ();

At that time, we didn't know what the construct of the sub-object such as X.C failed: such an object has never existed. The design of the design is so essentially of such a fundamental error (and what kind of behavior we are now not allowed)? The essence of C is really different at that time? Or our lives in the dream, I didn't think that X is really not forming, didn't there?

This issue is a bit too much because the C language is now the same language than the past. C of the old (abnormal support) as the current C as C is treated with C . Although they have the same syntax, they are different. take a look:

Struct X

{

X ()

{

P = new t; // Assume 'new' Fails

}

Void f ();

}

X x;

x.f ();

Suppose the New statement does not have successfully assigned a T object. Under the compiler (or an abnormal modern compiler) before the exception support, NEW returns NULL, X. The constructor and X.f () are called. However, after the exception allows, the NEW throws the usual, the X structure failed, and X.f () is not called. The same code, very different.

In the past, the object has no self-destructive ability, they must construct and depend on us to discover its state. They do not deal with the sub-objects that failure. Also, they do not invoke a library function that throws the usual library in the standard runtime. In short, the past procedures and the current procedures exist in different worlds. We cannot expect them to have the same reaction to the same error.

1.3 Is this your final answer?

I am now believed that the C standard behavior is correct: constructor throws an object that is processing the object and its inclusive object. I don't know if the C Standards Committee develops this behavior, but I guess:

The object of part of the constructor will result in some subtle mistakes because its user has more than actual assumptions. Different objects of the same class will have unexpected and unpredictable different behaviors.

l The compiler requires an additional record. When a part of the object disappears, the compiler avoids the sub-object call destructuring function to which it and its partially constructed.

l The equation of the object is constructed and the existence of the object will be broken, destroying the essence of C .

1.4 Guidance for users of the object

An exception is part of the interface of the object. If possible, you can prepare an exception set in advance. If an interface does not provide an exception specification, it is not possible to know its abnormal behavior from it elsewhere, then assume that it may give any anomalies at any time.

In other words, it is ready to capture or at least filtering all possible exceptions. Don't let any exceptions enter or leave your code without being expected; even if you just pass or reap out an exception, you must also be careful.

1.5 constructor throwing

Prepare the exception set of exceptions that all of the constructor of all sub-objects, and capture them in your constructor. Such as:

Struct a

{

A () throw (cHAR, int);

}

Struct B

{

B () throw (int);

}

Struct C

{

C () throw (long);

}

Struct X

{

A a a;

B B;

C C;

X ();

}

The abnormal set of sub-object constructor is {char, int, long}. It is the possible abnormality of the constructor of X. If the X's constructor is transmitted without filtering, it will be the abnormal specification.

X () throw (char, int, long);

But using the function try block, the constructor can map these exceptions to other types:

X () throw (unsigned)

Try

{

// ... x :: x bodyy

}

Catch (...)

{

// map caught sub-object exceptions to another type

Throw 1u; // Type Unsigned

}

As with the previous part, the user's constructor cannot block the abnormal propagation of the child object, but can control the type of passage, by the input of the anomaly map to the controlled transmission type (here is Unsigned).

1.6 Constructor does not leave the usual

If the constructor of the sub-object is throwing, its abnormal set is empty, indicating that the constructor of the inclusive object will not encounter an exception. The only way to determine your constructor does not leave the usual, is only a child object that does not leave the usual.

If you must be embarrassed to throw a child object, you still don't want to throw an exception from your own constructor, consider using the method called Handle Class or PIMPL ("PIMPL" twin: PIMPL or "Pointer to Implementation" ). It has been used as a technique that decreases compilation time, and it also improves abnormal safety.

Back to the previous example:

Class X

{

PUBLIC:

X ();

// ... Other X MEMBERS

Private:

A a a;

B B;

C C;

}

According to this method, X is divided into two separate parts. The first part is "public" header file referenced by the user of X:

Struct X_Implement;

Class X

{

PUBLIC:

X () throw ();

// ... Other X MEMBERS

Private:

Struct x_implementation * implemented;

}

The second part is private implementation

Struct x_implementation

{

A a a;

B B;

C C;

}

X :: x () throw ()

{

Try

{

Implementation = new x_implement;

}

Catch (...)

{

// ... Exception Handled, But Not Implicitly Reeth.

}

}

// ... Other X MEMBERS

X The constructor captures all the exceptions in the configuration * Implementation process (that is, the process of constructing A, B, and C). In addition, if the data member has changed, the X user does not need to recompile because the header file is not changed.

(Back question: If x :: x is captured an exception, * Implementation and at least one of the sub-objects A / B / C are not fully constructed. However, the object of the inclusive X X continues the life period as a valid entity. This X Partial structure of the object of the object violates C essence?)

Many C guidance manuals discuss this method, so I don't have it here. An extremely detailed discussion appears on the Items26-30 of "Exceptional C " in Herb Sutter.

1.7 Guidance to the object provider

Do not equate an exception system to a wrong handling system, think it and returns an error code or setting a global variable at the same level. Exceptions fundamentally changed the structure and significance of the code around it. They temporarily change the runtime language of the program, skip some commonly running code and activate other code that is not run. They force your program response and processing to cause the wrong state of the program death.

Therefore, the exception characteristics and simple error handling are different. If you don't want these characteristics, or don't understand these features, or don't want to write these features to documents, don't throw it out, use other error handling systems.

If you decide, you must understand all the causal relationships. Understand your decision has a huge potential impact on people who use your code. Your exception is part of your interface; you must write your interface in your document, will throw something, when you throw, and why. And put this document in an abnormal specifications declare.

1.8 constructor throwing

If your constructor throws anomalies, or you (direct or indirectly) tolerant a child object, the user object tolerant to your object will also throw anomalies and thus failed. This is the price of users who reuse your code. To make sure this cost is worth it.

You are not forced to throw it in the constructor, and the old method is still effective. When your constructor encounters an error, you must judge that these errors are fatal or slightly affected. Throw a constructed exception to pass a strong information: this object is destroyed and cannot be repaired. Returns a construction status code indicates a different information: this object is destroyed but has functions. Do not throw an impetus because it is a fashionable method: it will be self-destructive when an object is really can't or should not survive.

1.9 transaction

Don't let your interface to pass your job. If you know the exact abnormal set of your interface, you will be listed in the abnormal specifications. Otherwise, no abnormal specifications are declared. No abnormal specifications have declared better than lying abnormal specifications, because it won't deceive users.

The possible exception of this rule is: template exception. As the top three are written, the template writer usually does not know the abnormality that may be thrown. If your template does not provide abnormal specifications, users will reduce security and confidence. If your template has an abnormal specification, you must:

l Establishing the skills of exception safety before using the exceptions to ensure that the abnormal specifications are accurate

l Eth, write your template on the document, only accept the parameter type with certainty characteristics, and warn other types will result in out of control (With the Cavet That Other Types May Indu ".".

1.10 necessary vs full

Don't people with complexity of your class, just to adapt to all possible needs. Not all objects will be reused. If PET Becker is written to me:

The current programmer spent too much time to cope with what the possible things, and they should simply reject. If there is a reason to leave a good reason, boldly throw anomalies, and write a document, do not create some exquisite methods to avoid throwing these anomalies. Increased complexity may lead to the maintenance of the nightmare, which exceeds the suffering of the error to use the restricted version.

Pete's statement is equally useful to the destructor. Look at this principle (from part14):

Do not throw it in the destructor.

In general, this principle is better than violating it. But sometimes not this:

l If you are ready to let others enable your object, or at least not to prohibit others to include your object, then don't leave it in the destructive function.

l If you really have a reason to throw anomalies, and you know that it violates the security strategy, then boldly throws the usual, written in the documentation.

Just as an abnormal treatment must be considered when designing, it must also be considered. During the designer function, throw () is a necessary condition for a good sub-object, but it is not sufficient. You must forward what context will be given to your code, it will tolerate what will resist. If the complexity of the design is added, ensure that these complexities are part of the strategy, rather than fragile "in the case of preventing the case" insurance policy.

1.11 Thank you

(slightly)

In addition to some sporadic things, I have completed an unusual theme! In fact, I also completed an abnormal topic. The next time is suspended, and the C anomaly and the mixing of Visual C SEH will be discussed in March.

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

New Post(0)