第四章 表达式
第四章 表达式
字面值、变量、算术和逻辑运算式都属于表达式
4.1 基础
第一小节提到了运算符分类、类型转换、运算符重载、左右值等基本概念
C++运算符

类型转换
cppreference.com 包含关于自动类型转换的所有规定
[一般算术转换 - cppreference.com]介绍了这些常见情况下的整数到整数运算符的隐式转化规则
具体见4.11节
运算符重载
仅能实现运算对象与返回值类型的重载,并且不支持对优先级、结合律及运算对象个数的重载
见后文
左右值
Modern C++
Modern C++

左值 一般 可以代替右值使用,反过来不允许
优先级与结合律


用多了就知道结合律了,一般都带括号比较稳妥
求值顺序
只有**& &、||** 、?: 、, 四种运算符规定了求值顺序
其他运算符没有规定(有利有弊)
4.2 算术运算符
会进行一般算术转换(见上面类型转换);对象和结果都是右值 ;
溢出属于UB;
除法向0取整;取余运算规则是m%n=m - (m/n) * n
4.3 逻辑和关系运算符
&&和||都有短路 策略:左侧为真(假)才对右侧求值
- 练习4.10

4.4 赋值运算符
左侧必须是可修改的左值,返回类型也是左侧类型;
右侧可以是花括号(注意不要窄化int k={3.14})
优先级较低
4.5 递增递减运算符
后置版本优先级比*解引用高,因此会出现下面的简便写法

无论前置后置,尽量不要和其他运算符放在一起改变对象的值(UB)
4.6 成员访问运算符
有点和箭头;点优先级比*解引用高,注意写法
4.7 条件运算符?:
- 练习4.21

- 练习4.23
+比==优先级高,不能得出想要的结果

要在+后面加上括号,把后面的都括起来
4.8 位运算符
只作用于整数,类似
左移操作会将数值向左移动一位,并在右侧填充0;右移操作对无符号数进行右侧填充0,并对有符号数根据具体情况决定如何填充(我的运行环境是补充前导符号位)

4.9 sizeof运算符
sizeof (type)或者sizeof expr
返回值是size_t类型,不会计算表达式的值
有些特殊情况:
struct Empty { };
struct Base {
int a;
};
struct Derived : Base {
int b;
};
struct Bit {
unsigned bit : 1;
};
struct CharChar {
char c;
char c2;
};
struct CharCharInt {
char c;
char c2;
int i;
};
struct IntCharChar {
int i;
char c;
char c2;
};
struct CharIntChar {
char c;
int i;
char c2;
};
struct CharShortChar {
char c;
short s;
char c2;
};
int main()
{
Empty e;
Derived d;
Base& b = d;
[[maybe_unused]] Bit bit;
int a[10];
auto f = [&]() { return sizeof(int[10]) == sizeof a ? throw 1 : e; };
// f(); // 返回类型是 Empty,但是必然会抛出 1
auto println = [](auto rem, std::size_t size) { std::cout << rem << size << '\n'; };
//sizeof结果永不为0
println("1) 空类的大小: ", sizeof e); // 1
println("2) 指针的大小: ", sizeof &e); // 8
println("3) sizeof(Bit) -类: ", sizeof(Bit)); // 4
println("4) sizeof(int[10])-含有10个 int 的数组:", sizeof(int[10])); // 40
println("5) sizeof a -含有10个 int 的数组:", sizeof a); // 40
// 对数组运算得 数组元素总数*每个元素所占字节数
println("6) 含有10个 int 的数组的长度: ", ((sizeof a) / (sizeof *a))); // 10
println("7) 含有10个 int 的数组的长度 (2): ", ((sizeof a) / (sizeof a[0]))); // 10
println("8) Derived 的大小: ", sizeof d); // 8
println("9) 通过 Base 获取 Derived 的大小: ", sizeof b); // 4
println("A) sizeof(unsigned): ", sizeof(unsigned)); // 4
println("B) sizeof(int): ", sizeof(int)); // 4
println("C) sizeof(short): ", sizeof(short)); // 2
println("D) sizeof(char): ", sizeof(char)); // 1
// 对char求大小永远为1
println("E) sizeof(CharChar): ", sizeof(CharChar)); // 2
println("F) sizeof(CharCharInt): ", sizeof(CharCharInt)); // 8
println("G) sizeof(IntCharChar): ", sizeof(IntCharChar)); // 8
println("H) sizeof(CharIntChar): ", sizeof(CharIntChar)); // 12
println("I) sizeof(CharShortChar): ", sizeof(CharShortChar)); // 6
println("J) f() 的大小: ", sizeof f()); // 1
}
需要注意的是,在处理string或vector时使用sizeof函数仅能获取其在内存中的占用情况,并非直接反映数据元素的数量。具体来说,在这种情况下您将无法获得关于内存块长度、内存地址(指针)以及存储容量等其他相关信息。这些细节都是通过sizeof函数所无法捕捉到的。

- 练习4.28
The memory address occupied by a bool type is one byte; similarly, the same applies to char types, which also use one byte; the integer type occupies four_bytes; the float and double types occupy four and eight_bytes respectively; the long and signed-long types take up eight_bytes each; the unsigned-long and __int64 types also use eight_bytes; short integers are stored in two-byte locations; a wchar_t variable takes up four-byte space; finally, long doubles require sixteen-byte allocations for storage.
- 练习4.29

4.10 逗号运算符
返回值是最右侧表达式的值,类型同理
优先级最低(容易有题挖坑)
- 练习4.33
somevalue ? ++x, ++y : --x, --y实际上somevalue ? ++x, ++y : --x是一个整体条件运算
4.11 类型转换
隐式的转换过程往往出现在实参与形参类型或数量不一致的情况;运算符作用于的操作数类型不符的情况;初始化值与变量声明的类型不符的情况;以及条件判断的结果并非布尔类型的情况;此外,在switch语句使用的判别对象也不是整型的情况下也会发生隐式的转换过程。这些情形主要包括以下五种常见的情形:
算术转换
常规的算术转换规则在C++语言中得到了详细说明
转换阶段为:枚举、浮点、整型
在C++编程中对整数类型的优化与改进是实现高效数据处理的关键步骤;具体而言,在C++标准库中提供了丰富的数据类型支持,并且通过隐式转换 - cppreference.com这一资源可以深入理解相关机制
当无符号类型碰到有符号类型时:
- 无符号更大或两者一般大:
有符号数转换为无符号数
- 有符号更大:
看哪个类型更大就往哪里转换
例:

其他隐式转换
将数组元素转化为指向器对象
处理指向器对象之间的数据传递
将变量或对象强制解析为布尔值
统一使用基础类型的常量进行数据转化
在类体内实现特定的自定义数据类型映射,并且仅执行一次这样的映射操作
显式转换
C++里的长这样:cast_name
C里的长这样:(type)expr 或 type(expr)——不过会被视为使用cast
| static_cast | 不包含底层const的明确转换都可以用 | int i, j; double slope = static_cast |
|---|---|---|
| const_cast | 只能用来改变底层const(指针和引用) | const char* pc; char* p = const_cast<char*>(pc); auto newp = const_cast<const char*>(p); |
| reinterpret_cast | 转换指针和引用到另一种类型(按位重新解释) | int i = 7; bitset<32> a(i); cout << a << endl; // 00000000000000000000000000000111 char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "本系统是小端的\n" : "本系统是大端的\n"); //低位在前为小端存储 |
| dynamic_cast | 用于安全的运行时类型识别 (RTTI) 转换,只适用于类层次结构,通常与基类指针和派生类指针一起使用 | 后面会介绍 |
- 练习4.36

- 练习4.37

第四章结束
略琐碎,除了左右值,其他的规则用到的时候查就可以了
