Prefix and suffix of the auto-increment and auto-increment operators, and suffix of the operator prefix.
There is such a question in the exam:
Evaluate the value of a. The correct answer is 10.
If you think that the focus of this question is only the operator priority, you may easily get the correct answer.
However, have you considered why the following code cannot be compiled?
During the written test, I considered the left value of the expression as the value assignment operator, but I did not know much about the implementation mechanism and function prototype of the overloaded "++" operator, I mistakenly think that neither of the writing methods "a ++" nor "++ a" can be used as the left value of the value assignment operator, so as to think that this question has failed, or deliberately investigate this, I directly wrote a "unable to compile "......
After I came back, I asked two friends that they could all get the correct result of "(+ + a) + = a;", that is, a = (a + 1) + (a + 1 ), however, I cannot explain why "(a ++) + = a;" cannot be compiled, and I agree that "++ a" is "the result of self-increment execution first and then the return value, therefore, the expression is the value after auto increment, while "a ++" is the value before auto increment,After this statement is executed, a is automatically added."!
The above is basically correct about the pre-auto-increment operator, but it is a big mistake to know about the post-auto-increment operator! I despise these two friends here. Do you use the C language taught by PE teachers? It is assumed that an auto-incrementing operator can execute a part firstAfter the entire statement is executedExecute another part ...... (Which language has this function ?)
This morning, I took the time to study the content in this area, so I suddenly realized that I have gained a lot of understanding about the two forms of auto-increment/auto-increment operators. Do not dare to exclusive, the summary is as follows, also correct the friends of the wrong understanding @ shasha, hum.
.
I. Differences between the prefix and suffix of auto-increment and auto-increment Operators
We all know that the self-incrementing operator (++ operator) in C/C ++ has two forms: pre-operation and post-operation.
From the implementation of operators, the differences between a ++ and ++ a are as follows:
(1) The first increment operation is a ++. During execution, the object is incrementally modified, and then the reference of the object is returned.
(2) The post-increment operation ++ a is written in the method returned by the value in the operator overload function. An object is created inside the overload function to temporarily store the original object value, after the auto-increment of a is executed, the function returns the value of the temporary object.
To put it simply, it is:
The First Auto-increment operation generates the left value. After adding 1 to the operand, the changed operation value is returned. Then, the auto-increment operation generates the right value, add 1 to the operand, but return the original value of the unchanged operand.
The concept of left and right values is attached:
Left value: the value that can appear on the left of the value assignment operation. Non-const left values can be read and written.
Right value: the right side of a value assignment operation, but not the left side. The right value can only be read and cannot be written.
Therefore, the left value can appear on the right side of the value assignment operation, but the right value cannot appear on the left side of the value assignment operation. If the post-auto-increment operation is placed on the left side of the value assignment operation, a compilation error will occur.
Now let's analyze why "(a ++) + = a;" cannot be compiled: For "(a ++) + = ;", first (a ++), but (a ++) returns a copy of the original a value instead of a reference. At this time, the copy is no longer a variable or a constant, therefore, the operation cannot proceed as the left value.
Likewise, this can also explain whether the following expressions can be compiled:
(1) ++;
(2) a ++;
(3) ++ a ++;
We know that the auto-increment operator ++ is the combination direction from right to left (VC ++ 6.0), so ++ a is also written as ++ (++ ), obviously, this is correct. A ++ writing (a ++) ++ is obviously incorrect, which leads to compilation errors:
Error C2105: '+ +' needs l-value.
As for ++ a ++, writing ++ (a ++) based on combination is also incorrect, but note that (++) ++ is the correct method.
.
Ii. Reload of ++ Operators
A long time ago (January 1980s), there was no way to distinguish the prefixes and suffixes of ++ and -- operators. This problem was complained by programmers, so the C ++ language was extended and allowed to reload the increment and decrement Operators in two forms.
However, there is a syntax problem. The differences between overload functions depend on the differences in their parameter types. However, whether it is the increment or decrement prefix or suffix, there is only one parameter. To solve this language problem, C ++ requires an int type parameter in the form of a suffix. When a function is called, the compiler passes a value 0 as the int parameter to the function:
12345678910111213141516 |
class UPInt { // "unlimited precision int" public : UPInt& operator++(); // ++ Prefix const UPInt operator++( int ); // ++ Suffix UPInt& operator--(); // -- Prefix const UPInt operator--( int ); // -- Suffix UPInt& operator+=( int ); // + = Operator, UPInts and ints operations ... }; UPInt i; ++i; // Call I. operator ++ (); i++; // Call I. operator ++ (0 ); --i; // Call I. operator --(); i--; // Call I. operator -- (0 ); |
This rule is a bit odd, but you will get used to it. Note that the prefix of these operators is different from the type of return values in the suffix format. Prefix returns a reference, and suffix returns a const type. The prefix and suffix form of the ++ operator will be discussed below. These instructions also use the -- operator.
But from the day you started as a C programmer, you should remember that the prefix form of increment is sometimes called "add and retrieve", and the suffix form is called "retrieve and add ". These two statements are very important because they are formal specifications of the increment prefix and suffix.
1234567891011121314 |
// prefix: increment and then fetch UPInt& UPInt::operator++() { * this += 1; // Add return * this ; // Retrieve the value } // postfix form: fetch the origin and increment const UPInt UPInt::operator++( int ) { UPInt oldValue = * this ; // Retrieve the value ++(* this ); // Add return oldValue; // Return the retrieved Value } |
The suffix operator function does not use its parameters. Its parameters are only used to distinguish between prefix and suffix function calls. If you do not use parameters in a function, many compilers will display warning information, which is annoying. To avoid this warning, the parameter name you do not want to use is omitted in a frequently used method, as shown above.
Obviously, an increment extension must return an object (it returns the value before adding), but why is it a const object? If it is not a const object, the following code is correct:
12 |
UPInt i; i++++; // Two increment suffix operations |
This is completely achievable, but we should try our best to avoid it! Why? Because the above Code can actually be rewritten to the following form:
1 |
i.operator++(0).operator++(0); |
Obviously, the object returned by the First operator ++ function calls the second operator ++ function.
There are two reasons for us to dislike this practice.
The first is that the results produced by using the two suffix increment are inconsistent with the caller's expectation.
As shown above, the value changed by the second call of operator ++ is the value of the object returned by the first call, rather than the value of the original object. Therefore, if the expression I ++ is valid, I will be added only once. This is contrary to human intuition and confusing.
The second reason is that it is inconsistent with the built-in type of language behavior. When designing a class, a good principle is to make the class Behavior consistent with that of the int type.
As shown in the previous example, the int type does not allow two consecutive increment suffixes. Therefore, you must also disable the class you write. The easiest way is to make the suffix increment return the const object. When the compiler executes "I ++", it will find that the const object returned from the first operator ++ function calls the operator ++ function, however, this function is a non-const member function, so the const object cannot call this function. If you have thought that it makes no sense for a function to return a const object, you now know that sometimes it is useful. The suffix increment and decrement are examples. (For more examples, see Objective C ++ Clause 21 .)
In addition, if you overload the ++ operator, you need to consider the maintenance issues:
Let's take a look at the suffix and prefix increment operators. In addition to the different return values, the functions are the same, that is, adding a value to one. In short, they are considered to have the same functionality. So how do you ensure that the suffix increment and prefix increment are consistent? When different programmers maintain and upgrade code, what can ensure they will not be different? This can be ensured unless you follow the principles in the above Code. This principle is: the suffix increment and decrement should be implemented according to their prefix format (rather than using additional forms such as * this + = 1 ;).
In this case, you only need to maintain the prefix version because the suffix format is automatically consistent with the prefix format.
.
Iii. Efficiency differences between prefixes and suffixes
If you are very concerned about efficiency, when you see the suffix increment function, you may think there are some problems. This function must create a temporary object as its return value. For example, the preceding Implementation Code creates a temporary object (oldValue) to be displayed ), this temporary object must be constructed and parsed at the end. The prefix increment function does not have such a temporary object.
The following is a surprising conclusion:Unless the original value of the operand must be saved, the caller should try to use the prefix increment instead of the suffix increment.
.
4. An interesting question: why C ++ instead of ++ C?
Exercise 5.16 In C ++ Primer mentions an interesting question:
Why do you think C ++ is not called C ++?
According to the analysis of this article on the blog site:
C ++ was named by Rick Mascitti in The summer of 1983 (see section 1.4 of The C ++ Programming Language (Special Edition). C indicates that it evolved from C Language in essence, "++" is an auto-increment operator in C language. C ++ is a superset of C language and is an Extension Based on C language. It introduces operators not found in C languages such as new and delete, and adds direct support for object-oriented programming, and so on). The C language is available first, and then ++ is performed. According to the difference between the pre-and post-forms of the auto-increment operator, C ++ indicates that after the C language is expanded, the C language content can also be used; writing in ++ C indicates that the original value of C cannot be used again. That is to say, C ++ cannot be backward compatible with C, which is inconsistent with the actual situation.
It's also a brand new perspective ......