An expression
C + + provides a rich operator and defines what these operators mean when the operands are of a built-in type. In addition, C + + supports operator overloading, allowing programmers to customize the meaning of the operator when used for class types. The standard library uses this function to define the operators for the library type.
This chapter focuses on the operators of the C + + language definition, which uses the operands of the built-in types, and also describes the operators of some of the standard library definitions. The 14th chapter will learn how to define your own overloaded operators.
An expression is composed of one or more operands that are combined by an operator. The simplest expression contains only one literal constant or variable. A more complex expression is composed of an operator and one or more operands.
Each expression produces a result. If there is no operator in an expression, the result is the value of the operand itself (for example, a literal constant or variable). When an object is used where its value is needed, the value of the object is evaluated. For example, suppose ival is an int object:
if (ival)//Evaluate ival as a condition
// ....
The preceding statement ival as a conditional expression for an if statement. If the ival is not a 0 value, if condition is set, otherwise the condition is not tenable.
For an expression containing an operator, its value is obtained by specifying the operand. In addition to special usages, the result of an expression is a right value that can be read, but is not allowed to be assigned.
The meaning of the operator-what the operator does and the type of the result of the operation-depends on the type of operand.
You cannot determine the meaning of a particular expression unless you know the type of the operand. The following expression
i + j
It may be an addition to an integer, a string of strings, or an addition to a floating-point number, or it could be another operation. How to calculate the value of the expression depends entirely on the data type of I and J.
C + + provides two operators, unary operators and two-dollar operators. An operator acting on an operand is called a unary operator, such as an address operator (&) and a reference operator (*), while a two-dollar operator acts on two operands, such as the addition operator (+) and the subtraction operator (-). In addition, C + + provides a ternary operator (ternary operator) that uses three operands, which we will introduce in section 5.7.
Some symbols (symbols) can represent both a unary operation and a two-dollar operation. For example, the symbol * can be either a (unary) dereference operator or a (two-yuan) multiplication operator, which is independent and irrelevant, and may be easier to understand if it is viewed as two different symbols. For such an operator, it is necessary to determine whether it represents a unary operation or a two-dollar operation, depending on the context in which the symbol is in place.
The operator has a requirement for the type of its operand, and if the operator applies to operands of a built-in or compound type, the C + + language defines its type requirements. For example, a dereference operator for a built-in type object requires its operand to be a pointer type, and the dereference of any other built-in type or composite type object will result in an error.
For a two-element operator with a built-in or compound type, it typically requires that its two operands have the same data type, or that its type can be converted to the same data type. For type conversions, we will study in section 5.12. Although the rules may be more complex, most type conversions can be done as expected. For example, an integral type can be converted to a floating-point type, and vice versa, but the pointer type cannot be converted to a floating-point type.
To understand an expression consisting of multiple operators, you must first understand the precedence, associativity, and
The order in which the operands are evaluated. For example, an expression
5 + 10 * 20/2;
addition, multiplication, and division operations are used. The value of the expression depends on how the operand is combined with the operator. For example, the operands of the multiplication operator * can be 10 and 20, or 10 and 20/2, or 15 and 20, 15, and 20/2. Associativity and precedence rules specify how operands and operators are combined. In the C + + language, the value of the expression should be 105,10 and 20, and then the result divided by 2, plus 5 is the final result.
When solving an expression, it is not enough to understand only how operands and operators are combined, but also the order in which each operand on the operator is evaluated. Each operator controls its assumed order of evaluation, that is, whether we can assume that the left operand is always evaluated before the right-hand operand. Most operators cannot guarantee a particular order of evaluation, and we will discuss this issue in section 5.10.
5.1. Arithmetic operator
Unless otherwise stated, the operator shown in table 5-1 can be used for any arithmetic type (sect. 2.1) or for any data type that can be converted to an arithmetic type.
Table 5.1 Group operators by priority-unary operators have the highest precedence, followed by multiplication, division, and then two-dollar addition and subtraction. Higher-priority operators are more tightly coupled than low-priority. These arithmetic operators are left-bound, which means that when the operator's precedence is the same, these operators are combined from left to right, in turn, with operands.
Table 5.1. Arithmetic operator
Operator Functional Usage
+ unary plus (unary plus) + expr
-Unary minus (unary minus)-expr
* Multiplication (multiplication) expr * expr
/Division (Division) expr/expr
% remainder (remainder) expr% expr
+ addition (addition) expr+ expr
-Subtraction (subtraction) expr–expr
For the aforementioned expression
5 + 10 * 20/2;
Considering the priority and the binding, it is known that the expression to do multiplication (*) operations, its operands are 10 and 20, and then the result of the operation and 2 for the operands of the Division (/) operation, the result and the operation of 5 to do addition (+) operation.
The unary minus operator has an intuitive meaning, and it takes a negative number of its operands:
int i = 1024;
int k =-I; Negates the value of Itsoperand
The unary plus operator returns the operand itself, without any modification to the operand.
Warning: Overflow and other arithmetic exceptions
Some arithmetic expressions are not defined, and some of them are caused by mathematical attributes, such as 0 operations; others are attributed to computer characteristics, such as overflow: The calculated value is beyond the range of its type.
Consider a machine with a short type of 16 digits and a maximum value of 32767. Assuming the short type has only 16 digits, the following composite assignment operation will overflow:
Max value if shorts are 8 bits
Short short_value = 32767;
Short ival = 1;
This calculation overflows
Short_value + = ival;
cout << "Short_value:" << short_value << Endl;
Indicates that the 32768 signed number requires 17 bits of storage space, but there are only 16 bits here, causing the overflow phenomenon, at which point many systems do not give a warning at compile time or at runtime. For different machines, the Short_value variable of the above example actually gets the same value. After executing the program on our system, we will get:
Short_value:-32768
Its value, truncated (wrapped around), sets the value of the symbol bit from 0 to 1, and the result becomes negative. Because the arithmetic type has a finite length, it is often the case that the calculated overflow occurs.
The binary +,-operator can also be used for pointer values, and the use of these operators for pointers will be described in section 4th 2.4.
Arithmetic operators + 、-、 * and/have intuitive meanings: addition, subtraction, multiplication, and division. Divide by two integers, and the result is still an integer, and if its quotient contains a decimal part, the fractional portion is truncated:
int ival1 = 21/6; Integral resultobtained by truncating the
Remainder
int ival2 = 21/7; No remainder, resultis an integral value
Both Ival1 and Ival2 were initialized to 3. Calculates the remainder of the left operand divided by the right-hand operand. The operands of the operator can only be integral, including bool, char, short, int, and long, and corresponding unsigned types:
int ival = 42;
Double dval = 3.14;
Ival% 12; Ok:returns 6
Ival% Dval; Error:floating Pointoperand
If the two operands are positive, the result of the Division (/) and modulo (%) operation is also positive (or 0); If two operands are negative, the result of the division operation is positive (or 0), and the result of the modulo operation is a negative number (or 0), and if only one operand is negative, the result of both operations depends on the machine ; the symbol for the modulo result also depends on the machine, while the value of the division operation is negative (or 0):
21% 6; Ok:result is 3
21% 7; Ok:result is 0
-21%-8; Ok:result is-5
%-5//Machine-dependent:result is 1 or-4
21/6; Ok:result is 3
21/7; Ok:result is 3
-21/-8; Ok:result is 2
21/-5; Machine-dependent:result-4 or-5
When only one operand is negative, the symbol for the result value of the modulo operation depends on the symbol of the numerator (dividend) or denominator (divisor). If the result of the modulus is calculated with the symbol of the molecule, the value of the addition is rounded to the 01 side; If the modulus matches the symbol of the denominator, the value of the addition is rounded to the negative infinity side.
5.2. Relational operators and logical operators
Relational operators and logical operators (table 5.2) use operands of arithmetic or pointer types, and return values of type bool.
Table 5.2. Relational operators and logical operators
The following operators all produce bool values
Operator Functional Usage
! Logical not (logical non)!expr
< less than (less than) expr < expr
<= less than or equal (less than equals) expr <= expr
> Greater than (greater than) expr > expr
>= greater than or equal (greater than equals) expr >= expr
The following operators all produce bool values
Operator Functional Usage
= = Equality (equal) expr = = Expr
!= inequality (unequal) expr!= expr
&& Logical AND (logic and) expr && expr
|| Logical OR (logical OR) expr | | Expr
logic and, logic, or operator
A logical operator treats its operands as a conditional expression (1th. 4.1): Evaluates the operand first, or false (false) if the result is 0, or True (true). The result is true only if the two operands of the logical and (&&) operator are true. For logic or (| | operator, the value of true if one of the two operands is true. Given the following form:
EXPR1 && EXPR2//Logical AND
Expr1 | | EXPR2//Logical OR
EXPR2 is solved only if the value of an expression cannot be determined by EXPR1. In other words, when and only
You must ensure that EXPR2 can be calculated when the following conditions are present:
• In logic and expression, EXPR1 evaluates to True. If the value of EXPR1 is
False, the value of the logic and expression is false regardless of the value of the EXPR2. When
When the value of EXPR1 is true, only the value of EXPR2 is true, and the logical and expression
Value is true.
• In a logical or an expression, the EXPR1 evaluates to False. If the value of Expr1 is false, the value of the logical or expression depends on whether the EXPR2 value is true. The logical and logical OR operator always evaluates its left operand before calculating its right-hand operand. The right-hand operand is solved only if the value of the left-hand operand cannot determine the result of the logical expression. We often call this evaluation strategy "short-circuit evaluation (short-circuit evaluation)".
A valuable use for logic and operators is that if a boundary condition makes the calculation of expr2 dangerous, let Expr1 evaluate to false before the condition appears. For example, the writer uses a string object to store a sentence and then capitalizes all the characters in the first word of the sentence, which can be implemented as follows:
String S ("Expressions in C + + arecomposed ...");
String::iterator it = S.begin ();
Convert the word in S to uppercase
while (it!= s.end () &&!isspace (*it)) {
*it = ToUpper (*it); ToUpper covered Insection 3.2.4 (p. 88)
++it;
}
In this example, the while loop judges two conditions. First check that it has reached the end of the string type object, and if not, it points to a character in S. Only when the test condition is established does the system compute the right-hand operand of the logic and operator, that is, after it is guaranteed that it does point to a real character, then it checks whether the character is a space. If a space is encountered, or if there is no space in S and the end of S is reached, the loop ends.
logical non-operator
The logical non-operator (!) treats its operands as a conditional expression, producing a conditional value that is the opposite of its operand value. If the operand is a value other than 0, do it! The result is false after the operation. For example, you can use a logical non-operator on a vector-type object's empty member function to determine whether the object is empty based on the function return value:
Assign value of the A-in-VEC tox if there is one
int x = 0;
if (!vec.empty ())
x = *vec.begin ();
If the call to the empty function returns false, the child expression
The value of!vec.empty () is true.
The use of relational operators should not be threaded
Relational operators (<, <=, >, <=) have a left-bound feature. In fact, because the relational operator returns the result of the bool type, it rarely uses its left binding attribute. If multiple relational operators are used in tandem, the results are often unexpected:
oops! This condition does is not determineif the 3 values are unequal
if (I < J < k) {/* ... */}
The value of this expression is true as long as K is greater than 1. This is because the second less than the left-hand operand of the operator is the result of the first less than the operator: TRUE or false. That is, the condition compares K with an integer 0 or 1. In order to achieve the conditions we want to test, the above expression should be rewritten as follows:
if (I < J && J < K) {/* ... */}
equality test with BOOL literal
As described in section 5.12.2, the bool type can be converted to any arithmetic type--bool value false with 0, and true to 1.
Because true is converted to 1, it is often difficult to correctly determine whether a value is equal to the bool value true:
if (val = = true) {/* ... */}
Val itself is of type bool, or Val has a data type that can be converted to bool type. If Val is of type bool, the judgment condition is equivalent to:
if (val) {/* ... */}
Such code is shorter and more straightforward (although for beginners, such abbreviations can be confusing).
More importantly, if Val is not bool, the comparison of Val and true is equivalent to:
if (val = = 1) {/* ... */}
This is completely different from the following conditions:
Condition succeeds if Val are any Nonzerovalue
if (val) {/* ... */}
At this point, the condition evaluates to true as long as Val is any non 0 value. If you explicitly write a conditional comparison, the condition is valid only if Val equals the specified 1 value.