北京网站建设大概需要多少钱Seo与网站推广的技术对比
第 4 章 表达式
一、表达式基础
A、表达式: 由一到多个操作数组成,可以求值并 ( 通常会 ) 返回求值结果:
#include <iostream>
int main(){int x;x = 3;
}
- 最基本的表达式:变量、字面值
- 通常来说,表达式会包含操作符(运算符), 且可以嵌套
- 操作符的特性
 ● 接收几个操作数:一元、二元、三元 ?:
 ● 操作数的类型 - 类型转换
 ● 操作数是左值还是右值
 ● 结果的类型
 ● 结果是左值还是右值
 ● 优先级与结合性 (cpp-reference) ,可以通过小括号来改变运算顺序,和数学上一样,相同的优先级的结合性相同
 ● 操作符的重载 不改变接收操作数的个数、优先级与结合性
- 操作数求值顺序的不确定性
最终的顺序:
括号 -> 优先级 -> 结合性
PS: 函数调用也是表达式:
#include<iostream>
void fun(int p12, int p2)
{std::cout<< p1 << ' ' << p2 << '\n';
}
int main(){int x=0;fun(x=x+1,x=x+1); 
}
函数参数计算顺序可能不同
B、左值和右值
 传统的左值与右值划分
– 来源于 C 语言:左值可能放在等号左边;右值只能放在等号右边
– 在 C++ 中,左值也不一定能放在等号左边;右值也可能放在等号左边
● 所有的划分都是针对表达式的,不是针对对象或数值,针对表达式结果
 – glvalue :泛左值:标识一个对象、位或函数
 – prvalue :纯右值:用于初始化对象或作为操作数
 – xvalue : 将亡值:表示其资源可以被重新使用,在内存销毁前把内容保留下来
 
 decltype(实体/表达式)
 
 ● 左值与右值的转换
 – 左值转换为右值( lvalue to rvalue conversion )
 – 临时具体化( Temporary Materialization )
 ● 再论 decltype
 – prvalue → type
 – lvalue → type&
 – xvalue → type&&
std::move()可以构造亡值
C、类型转换
 一些操作符要求其操作数具有特定的类型,或者具有相同的类型,此时可能产生类型转换
 1、 隐式类型转换
– 自动发生
– 实际上是一个(有限长度的)转型序列
– https://en.cppreference.com/w/cpp/language/implicit_conversion
– 并非所有类型之间都可以任意转换
数值提升:精度、内存空间提升;数值之间的转换可以导致精度缺失,更改值
2、显示类型转换
 使用的时候小心谨慎些,容易出错,风险较大;尽量写长些,容易识别
– 显式引入的转换  可以使用括号和类型结合转换
– static_cast
– const_cast
– dynamic_cast
– reinterpret_cast
– C 形式的类型转换
关于类型转换的补充:
静态转换(Static Cast):
 静态转换是最常用的类型转换方式之一,用于显式转换一种类型为另一种类型,但在转换时没有进行运行时检查。静态转换可以用于类层次结构中的上下转换(派生类向基类转换),以及非相关类型之间的转换。使用静态转换时,需要注意类型转换的安全性,因为它没有运行时检查。示例:
int x = 10;
double y = static_cast<double>(x);  // 将整数转换为浮点数
动态转换(Dynamic Cast):
 动态转换用于在类层次结构中进行安全的上下转换(派生类向基类转换),并提供运行时类型检查。如果转换无效,即源指针指向的对象不是目标类型的一个有效派生类对象,动态转换将返回空指针(对于指针类型)或引发 std::bad_cast 异常(对于引用类型)。示例:
Base* basePtr = new Derived();  // 派生类指针赋值给基类指针
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);  // 动态转换
if (derivedPtr != nullptr) {// 转换成功,可以安全使用 derivedPtrderivedPtr->doSomething();
}
重新解释转换(Reinterpret Cast):
 重新解释转换是一种较为低级别的转换方式,用于将一个指针或引用转换为不同类型的指针或引用,甚至可以将整数类型转换为指针类型或反之。它的行为比较危险,需要谨慎使用,因为它不会进行任何类型检查和转换。示例:
int x = 10;
double* ptr = reinterpret_cast<double*>(&x);  // 将整数指针转换为双精度浮点数指针
常量转换(Const Cast):
 常量转换用于移除表达式的常量性(const)或将常量性添加到表达式。它主要用于处理函数重载或操作函数返回类型为常量的情况。常量转换不能用于修改实际的常量对象。示例:
const int x = 10;
int* ptr = const_cast<int*>(&x);  // 移除常量性以获取非常量指针
*ptr = 20;  // 修改通过常量指针获取的值
使用Boost将数字型对象的值转换为字符文本格式:boost::lexical_cast
在进行类型转换时,应谨慎考虑类型安全性和潜在的运行时错误
二、表达式详述
A、算术操作符
 ● 共分为三个优先级
 – + , - (一元,正负号),可以让数组变成指针,类型提升
 – * , / , % (%只接受整数)
 – + , - (二元,加减号)
 ● 均为左结合的
 ● 通常来说,操作数与结果均为算数类型的右值;但加减法与一元 + 可接收指针
 ● 一元 + 操作符会产生 integral promotion
 ● 整数相除会产生整数,向 0 取整
 ● 求余只能接收整数类型操作数,结果符号与第一个操作数相同
 ● 满足 (m / n) * n + m % n == m
B、逻辑与关系操作符
 ● 关系操作符接收算术或指针类型操作数;逻辑操作符接收可转换为 bool 值的操作数
 ● 操作数与结果均为右值(结果类型为 bool )
 ● 除逻辑非外,其它操作符都是左结合的
 ● 逻辑与、逻辑或具有短路特性
 ● 逻辑与的优先级高于逻辑或,建议使用括号!
 ● 通常来说,不能将多个关系操作符串连 例子:c>b>a ×
 ● 不要写出 val == true 这样的代码
 ● Spaceship operator: <=>用于比较复杂的对象的对比(C++20)
 – strong_ordering
 – weak_ordering
 – partial_ordering nan:not a number 非正常数字
c、位操作符
 ~:按位取反
 
 
● 接收右值,进行位运算,返回右值
 ● 除取反外,其它运算符均为左结合的
 ● 注意计算过程中可能会涉及到整形提升 integral promotion
 ● 注意这里** **
 ● 移位操作在一定情况下等价于乘(移位 :左移<< 1(乘2的1次)、右移>>1(除2)) 2 的幂,但速度更快
 <<n : 乘2的n次 >>n : 除2的n次
 ● 注意整数的符号与位操作符的相关影响
 – integral promotion 会根据整数的符号影响其结果
 – 右移保持符号,但左移不能保证
D、赋值操作符
 ● 左操作数为可修改左值;右操作数为右值,可以转换为左操作数的类型
 ● 赋值操作符是右结合的:先处理右侧的
 ● 求值结果为左操作数
 ● 可以引入大括号(初始化列表)以防止收缩转换( narrowing conversion )
 ● 小心区分 = 与 ==
 ● 复合赋值运算符
 交换x,y:
 
E、自增与自减运算符
 ● ++; –
 ● 分前缀与后缀两种情况
- 操作数为左值;前缀时返回左值操作完之后的;后缀时返回操作之前的右值
● 建议使用前缀形式
F、其它操作符
 ● 成员访问操作符: . 与 ->
 – -> 等价于 (*).
 – . 的左操作数是左值(或右值),返回左值(或右值 xvalue ),和返回类型相同
 – -> 的左操作数指针,返回左值
 ● 条件操作符
 – 唯一的三元操作符: ?:,可以嵌套
 
– 接收一个可转换为 bool 的表达式与两个类型相同的表达式,只有一个表达式会被求值
 – 如果表达式均是左值,那么就返回左值,否则返回右值
 – 右结合,嵌套多层的?:会先转换成 单个的 ?:
G:其他操作符:
 逗号操作符
 – 确保操作数会被从左向右求值
 – 求值结果为右操作数
 – 左结合
 ●sizeof 操作符
 – 操作数可以是一个类型(需要加括号)或一个表达式
 – 并不会实际求值,而是返回相应的字节尺寸
 ● 其它操作符
 – 域解析操作符 ::
 – 函数调用操作符 ()
 – 索引操作符 []
 – 抛出异常操作符 throw
 – …
在 C++17 中,可以确保 e1 会先于 e2 被求值
 – e1[e2]
 – e1.e2
 – e1.e2
 – e1→e2
 – e1<<e2
 – e1>>e2
 – e2 = e1 / e2 += e1 / e2 *= e1… (赋值及赋值相关的复合运算)
 ● new Type(e) 会确保 e 会在分配内存之后求值
