C++ study notes (4) - expressions, operators and type conversions

C++ study notes (4) - expressions, operators and type conversions

An expression consists of one or more operands, and evaluating the expression yields a result. Literal values ​​and variables are the simplest expressions, and the result is the value of the literal value and variable. Combining operators and operands can generate more complex expressions

Base

basic concept

  • For a complex expression with multiple operators, to understand its meaning, it is necessary to understand the operator's precedence, associativity and the order of operation of the operands.

  • During expression evaluation, operands are usually converted from one type to another.

  • lvalue: the identity of the object, that is, its location in memory

    • The assignment operator expects a (non-const) lvalue as its left-hand operand, and the result is also an lvalue
    • Takes an lvalue operand in the address scope and returns a pointer to the operand, which is an rvalue
    • The evaluation results of the built-in dereference operator and subscript operator are all lvalues
    • Increment decrement operator scoped lvalue operands for built-in types and iterators
  • rvalue: the value (content) of an object

  • The keyword decltypeacts on an lvalue, and the result is a reference type

    int a = 0;
    int *p = &a;
    decltype(*p) b;  // 解引用运算符是生成左值,所以 b 结果是 int &, 即引用类型,未初始化
    decltype(&p) c;  // 取地址符生成右值,所以 c 结果是 int **, 指针的指针

Evaluation order

Precedence dictates how operands are combined, but does not specify the order in which the operands are evaluated. If an expression points to and modifies the same object, an error is raised and undefined behavior occurs.

int i = f1() * f2();  // f1 和 f2 会在乘法之前调用,但却不知道 f1 和 f2 的执行顺序
int i = 0;
cout << i << " " << ++i << endl;  // 未定义,可能输出1 1,也可能是0 1

arithmetic operators

write picture description here
* Arithmetic operators can act on any arithmetic type, and any type that can be converted to an arithmetic type. Both operands and evaluation results of arithmetic operators are rvalues

  • The unary minus operator negates the value of the operand and returns its (promoted) copy

    int i = 1024;
    int k = -1;  // k 是 -2014
    bool b = true;
    bool b2 = -b;  // b2 是 true
  • The Boolean value does not participate in the operation. As shown in the above code, boolthe operation formation of the type is first promoted to the inttype 1, and the negative or is -1, not 0, so it b2is true

  • Remainder: If m % nis not equal 0, its negative sum is the msame.

Logical and relational operators

logistic_and_relation_operator

  • For both types of operators, both the operand and the result of the evaluation are lvalues
  • Short-circuit evaluation: that is, the value of the right-hand operand is evaluated if and only if the left-hand operand cannot determine the result of the expression, for logical AND and logical OR operators.
  • When performing comparison operations, do not use boolean literals trueand falseas operands unless the objects being compared are of boolean type

assignment operator

  • The left-hand operand of an assignment operator must be a modifiable lvalue
  • For compound operators ( +=, -=etc.), they are all completely equivalent a = a op b;, the only difference being the number of evaluations of the left-hand operand: only once with compound operators, twice with normal operators (one evaluation, one assignment)

Increment and decrement operators, member access operators, conditional operators

  • Pre-version (++a): returns the object itself as an lvalue
  • Postversion (a++): returns a copy of the object's original value as an rvalue
auto pbeg = v.begin();
*pbeg++;  // 正确, 返回*pbeg, 然后++pbeg; 因为后置递增运算符优先级高于解引用运算符

string s1 = "a string", *p = &s1;
*p.size();  // 错误: p是一个指针,没有名为size的成员, 因为解引用运算符优先级低于点运算符

string finalgrade = (grade < 60) ? "fail" : "pass";

bitwise operators

bit_operator

  • Shift Left: Insert 0 to the right
  • right shift: depends on the type of its left operand
    • unsigned type: insert 0 on the left
    • Signed type: a copy of the left caret bit or the value 0, depending on the environment

Note : Bit operation is a very important knowledge point and requires in-depth study

sizeof operator

sizeofThe operator returns the number of bytes occupied by an expression or a type name, and the result is a size_tconstant expression of a type

  • sizeofThe operand of an operator has two forms
    • sizeof (type)
    • sizeof expr: Returns the size of the result type of the expression. sizeofdoes not actually compute the value of its operands
  • It is still safe behavior to dereference an invalid pointer in sizeofthe operand of , because the pointer is not actually used, and sizeofyou don't need to actually dereference the pointer to know the type of the object it refers to.
  • sizeofThe result of an operator depends in part on the type it acts on:
    • charor chartype expression:1
    • Reference type: the size of the space occupied by the referenced object
    • Pointer: The size of the space occupied by the pointer itself
    • Dereference pointer: the size of the space occupied by the object pointed to by the pointer, the pointer does not need to be valid
    • Array: The size of the space occupied by the entire array, the sizeofoperation will not convert the array into a pointer for processing
    • stringObject or vectorobject: the size of the Fuding part of this type, not counting how much space is occupied by the elements in the object

type conversion

The C++ language does not directly manipulate two values ​​of different types, but first tries to unify the types of the operands according to the type conversion rules and then calculates. Because it is automatically executed, it is called implicit conversion.

When do implicit type conversions occur

  • In most expressions, intinteger values ​​smaller than the type are first promoted to the larger integer type
  • In a condition, a non-boolean value is converted to a boolean type
  • In the initialization process, the initial value is converted into the type of the variable; in the assignment statement, the right operand is converted into the type of the left operand
  • If the operand of an arithmetic operation or relational operation has multiple types, it needs to be converted to the same type.
  • Type conversion also occurs in function calls (learning later)

Arithmetic conversion

Arithmetic conversion is the conversion of one arithmetic type to another arithmetic type

  • The operand of the operator is converted to the widest type. For example, if one operand is long double, the other operand must be converted tolong double
  • When an expression has both floating-point and integer types, the integer value is converted to the corresponding floating-point type
  • Integer promotion: conversion of small integer types to larger integer types
    • bool, char, signed char, unsigned char, short, unsigned short, if they intfit, they are promoted to type int, otherwise they are promoted to unsigned inttype
    • Larger char types ( wchar_t, char16_t, char32__t) are promoted to the smallest of int, unsigned int, long, `….. etc., provided that they fit
  • An operand is an unsigned type
    • Unsigned >= Signed type: The signed operand is converted to unsigned. If the signed value is negative, the result cannot be judged
    • unsigned < signed: the result of the conversion is machine-dependent,
    • If all values ​​of the unsigned type can exist in the signed type, the unsigned operand is converted to the signed type
    • If not, the operand of the signed type is converted to an unsigned type

Other implicit type conversions

  • Array to pointer: In most expressions that use arrays, the array is automatically converted to a pointer to the first element of the array
  • pointer conversion
    • Constant integer values 0​​or literals nullptrcan be converted to any pointer type
    • A pointer to any non-constant can be converted tovoid *
    • A pointer to an arbitrary object can be converted toconst void *

show transition

named casts cast-name<type>(expression);, where cast-nameare static_cast, dynamic_cast, const_castandreinterpret_cast

static_cast

Any type conversion with a well-defined definition, as long as the underlying const is not included , can be usedstatic_cast

  • Useful when you need to assign a larger arithmetic type to a smaller type, the static_castcast means: I know and don't care about the loss of precision, so the compiler doesn't warn me

  • static_castCan be used for type conversions that the compiler cannot perform automatically

    void *p = &d;  // 正确:任何非常量对象的地址都能存入void *
    double *dp = static_cast<double*>(p);  // 正确:将void *转换成初始的指针类型.如果类型不符,则未定义

const_cast

  • Only the underlying layer of the operand can be changed const, that is, the behavior that can be used to convert a constant object to a non-const object.

    Once the properties of an object are removed const, the compiler no longer prevents us from writing to that object. If the object itself is not a constant, it is legal to use a cast to obtain write permission. But if the object is a constant, const_castperforming a write operation with it has undefined consequences.

  • Any other form of named cast that changes the constant attribute of an expression will raise a compiler error

  • const_castOften used in contexts with function overloading

reinterpret_cast

A lower-level reinterpretation is usually provided for the bit pattern of the operand. reinterpret_castEssentially machine-dependent, very dangerous, requires a good understanding of the types involved and the process by which the compiler implements the conversion

int *ip;
char *pc = reinterpret_cast<char*>(ip);  // 等价于 char *pc = (char *) ip;
string str(pc);  // 运行时错误,pc 实质上指的是一个int

dynamic_cast

  • dynamic_castThe usage form is as follows, typemust be a class type, and usually contains virtual functions

    • dynamic_cast<type*>(e): e must be a valid pointer
    • dynamic_cast<type&>(e): e must be an lvalue
    • dynamic_cast<type&&>(e): e cannot make lvalue
  • The type of e must meet any of the following three conditions

    • typeCommonly derived classes of target
    • target typecommon base class
    • typetype of target
  • When conversion fails

    • If the target is a pointer type: the result is 0
    • If the target is a reference type: throw bad_castan exception
    // 指针类型的 dynamic_cast
    // bp 指针指向 Base(至少含有一个虚函数), Derived 是 Base 的共有派生类
    if(Derived *dp = dynamic_cast<Derived*>(bp))
    {
      // 转换成功, 使用 dp 指向的 Derived 对象
    }else{  // bp 指向一个 Base 对象
      // 转换失败, 使用 dp 指向的 Base 对象
    }
    
    // 引用类型的dynamic_cast
    // 因为不存在控引用,对于引用失败,应该捕获异常
    void f(const Base &b)
    {
      try{
          const Derived &d = dynamic_cast<const Derived&>(b);
            // 使用 b 引用的 Derived 对象
      }catch(bad_cast){
          // 处理类型转换失败的情况
      }
    }

operator precedence table

operator_priority_table

Epilogue

  • For expressions with multiple operators, understand precedence, associativity, and order of evaluation
  • Type conversion is very important and needs to be studied in depth

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325629242&siteId=291194637