跳转到内容
新建笔记

运算优先级

类别运算符
算术运算符+, -, *, /, %, ++, --
关系运算符>, <, ==, >=, <=, !=
逻辑运算符!, &&, ``
位运算符<<, >>, ~, `, &, ^(不同为1)`
赋值运算符=, +=, -=, *=, =, %=, <<=, >>=, &=, `=, ^=`
条件运算符?:
逗号运算符,
指针运算符&, *
求字节数运算符sizeof
强制类型转换运算符(类型)
成员运算符., ->
下标运算符[]
其他函数调用运算符 ()
类别运算符优先级结合性
函数和表达式结构(), [], ->, .最高从左至右
一元运算符!, ~, ++, --, -, (类型), *, &, sizeof高从右至左
乘除法*, /, %中等从左至右
加减法+, -中等从左至右
位移<<, >>中等从左至右
关系<, <=, >, >=中等从左至右
相等性==, !=中等从左至右
位运算**&, ^(XOR), ``(并存时,优先级左至右)**中等从左至右
逻辑运算**&&, ``(并存时,优先级左至右)**中等从左至右
条件运算? :低从右至左
赋值**=, +=, -=, *=, /=, %=, >>=, <<=, &=, ^=, `=`**最低从右至左
逗号,最低从左至右

image-20240721043006936

*((volatile size_t*)gpioe_cfg1) &= ~(7 << 16); // 清除原配置
*((volatile size_t*)gpioe_cfg1) |= (1 << 16); // 设置为输出
*((volatile size_t*)gpioe_pul0) &= ~(3 << 16); // 清除上/下拉配置
*((volatile size_t*)gpioe_pul0) |= (1 << 12); // 设置为上拉
/*
~(7 << 16) 是一个位掩码,`7 << 16` 表示`7` 左移 `16` 位的结果。效果是将 GPIOE_CFG1 寄存器中第 `16` 位开始的 `3` 个位清零,而其他位不受影响。
00000000000000000000000000000111 原数字7
00000000000001110000000000000000 左移16位
11111111111110001111111111111111 取反
|= (1 << 16) 的效果是将原始整数中第 16 位设置为 1,而其他位保持不变。
00000000000000000000000000000001 原数字1
00000000000000010000000000000000 左移16位
*/
// 32 bit
unsigned a = 4;
int b = -16;
printf(a + b);
/*
负数在内存采用补码保存:
原码为10000000000000000000000000010000
反码为11111111111111111111111111101111
补码为11111111111111111111111111110000
a正数在内存采用原码保存:
原码为00000000000000000000000000000100
反码为00000000000000000000000000000100
反码为00000000000000000000000000000100
在内存中 a+b :
00000000000000000000000000000100 + 11111111111111111111111111110000
= 11111111111111111111111111110100
= 4,294,967,284
类型为 unsigned a
*/

原码表示方法:最高位为符号位,1表示负数,0表示正数。其余比特位表示数值。 反码表示方法:正数的反码是其本身,负数的反码为在原码的基础上,符号位不变,其余位取反。 补码表示方法:正数的补码就是其本身,负数的补码为在原码的基础上,符号位不变,其余位取反,再+1(即在反码的基础上+1)。

(1)运算符重载的用途

  1. 使自定义类型更直观:通过运算符重载,你可以使自定义类型的对象像内置类型一样进行操作,例如通过 + 运算符来合并两个自定义类型的对象。
  2. 提高代码可读性:对于某些操作,使用运算符可能比调用成员函数更直观,从而使代码更容易理解。
  3. 实现特定的行为:在某些情况下,你可能希望自定义类型的某些操作与内置类型不同,运算符重载提供了这种灵活性。

(2)基本格式

运算符重载通常作为类的成员函数或非成员函数实现。其基本格式如下:

作为成员函数

class ClassName {
public:
返回类型 operator运算符(参数列表) {
// 实现
}
};

作为非成员函数

返回类型 operator运算符(参数列表) {
// 实现
}

步骤

  1. 确定运算符:首先确定你想要重载的运算符。不是所有运算符都可以重载,有些(如 ?:)是不允许重载的。

  2. 选择成员或非成员:确定运算符重载是作为类的成员函数实现还是作为非成员函数实现。一些运算符(如赋值 =、下标 []、调用 () 和成员访问箭头 ->)必须作为成员函数重载。

  3. 编写函数体:实现运算符的具体功能。确保它符合运算符的预期行为和语义。

  4. 返回类型:根据需要选择适当的返回类型。通常,赋值和流操作符会返回引用,而算术运算符可能返回新对象。

(3)示例

重载加法运算符,作为成员函数:

class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
Point operator+(const Point& other) const {
return Point(x + other.x, y + other.y);
}
};

重载输出流运算符,作为非成员函数:

std::ostream& operator<<(std::ostream& os, const Point& point) {
os << "(" << point.x << ", " << point.y << ")";
return os;
}

注意事项

  • 确保重载的运算符行为符合直觉和标准运算符的预期行为。
  • 考虑运算符重载对类的封装性和接口的影响。
  • 考虑运算符的对称性(如 + 和 * 应该是对称的)。
  • 注意处理自赋值情况,尤其是在重载赋值运算符 = 时。

C++中的四种标准类型转换运算符

类型转换运算符说明用途代码示例
static_cast用于非多态的转换,编译时检查。基本数据类型转换,向上转型(如派生类指针到基类指针)。int i = 10; double d = static_cast<double>(i);
可以用于类层次结构中的转换,但仅限于向上转型。不能用于向下转型或改变对象的 const 属性。
不涉及多态性。
dynamic_cast用于类层次结构中的向下转型和多态性相关的转换,运行时检查。安全向下转型,需要类层次结构中有虚函数。Base* b = new Derived(); Derived* d = dynamic_cast<Derived*>(b);
涉及多态性。可以用于安全的向下转型。
转换失败时返回空指针(指针转换)或抛出异常(引用转换)。
const_cast用于移除或添加 const 属性,不改变值的类型。改变对象的 const 属性。const int* ci = new int(10); int* modifiable = const_cast<int*>(ci);
不涉及类型转换。可以用于修改 const 对象。
reinterpret_cast用于低级别的重新解释类型转换,如指针到整数的转换。强制类型转换,不安全。int* p = new int(20); char* ch = reinterpret_cast<char*>(p);
转换不安全,可能会丢失信息。用于指针和引用类型之间的转换,不推荐在普通类型转换中使用。

原则:

1)i++和++i都可视为一个带有返回的函数;

2)i++返回常量,++i返回变量i的引用或指针

i=1;
printf("%d", i++ + i++);
>> 1 + 2 = 3
printf("%d", i++ + ++i);
>> 1 + 3 = 4
printf("%d", ++i + ++i);
>> 3 + 3 = 6