操作符(Operators)
操作符(Operator)是 C++ 表达式的核心组成部分,用于对操作数(Operand)进行各种运算、比较或逻辑处理。 在编译阶段,操作符会被转化为相应的汇编指令或函数调用(例如运算符重载时)。
C++ 拥有丰富的操作符体系,并且允许程序员通过运算符重载(operator overloading)定义自定义类型的操作方式,从而实现与内置类型一致的自然语法。
一、操作符的分类
| 类别 | 示例 | 用途 |
|---|---|---|
| 算术操作符 | +, -, *, /, % | 数值计算 |
| 自增/自减操作符 | ++, -- | 累加或递减 |
| 关系操作符 | ==, !=, <, >, <=, >= | 比较 |
| 逻辑操作符 | &&, ||, ! | 逻辑判断 |
| 位操作符 | &, |, ^, ~, <<, >> | 位级运算 |
| 赋值操作符 | =, +=, -=, *=, /=, %= 等 | 赋值与复合赋值 |
| 条件运算符 | ?: | 三目条件判断 |
| 逗号运算符 | , | 顺序求值 |
| 成员操作符 | ., ->, .*, ->* | 成员访问 |
| 指针与地址操作符 | *, & | 指针解引用与取地址 |
| 类型转换操作符 | (type), static_cast, dynamic_cast 等 | 类型转换 |
| 其他特殊操作符 | sizeof, typeid, new, delete | 特殊用途 |
二、算术操作符(Arithmetic Operators)
1. 基本算术运算
| 操作符 | 含义 | 示例 |
|---|---|---|
+ | 加法 | a + b |
- | 减法 | a - b |
* | 乘法 | a * b |
/ | 除法 | a / b |
% | 取余(模运算) | a % b |
示例:
int a = 10, b = 3;
cout << a + b << endl; // 13
cout << a - b << endl; // 7
cout << a * b << endl; // 30
cout << a / b << endl; // 3
cout << a % b << endl; // 1
2. 注意事项
-
整数除法:两个整数相除结果仍为整数,小数部分会被截断。
-
浮点除法:若任一操作数为浮点型,则结果为浮点数。
-
取余运算
%:仅适用于整数类型。 -
负数取余:结果的符号与被除数相同,例如:
cout << (-7) % 4; // 输出 -3
三、自增与自减操作符(++ 与 --)
1. 区分前置与后置
| 形式 | 名称 | 求值顺序 | 示例 |
|---|---|---|---|
++i | 前置自增 | 先自增再返回值 | int a = 1; cout << ++a; // 输出 2 |
i++ | 后置自增 | 先返回值再自增 | int a = 1; cout << a++; // 输出 1 |
同理适用于 --。
2. 编译层面的区别
前置自增:
int& operator++(int& i) { i += 1; return i; }
后置自增:
int operator++(int& i, int) { int old = i; ++i; return old; }
因此后置版本通常会创建临时对象,性能略低。
四、关系操作符(Relational Operators)
用于比较两个值的大小或相等性,结果为 bool 类型。
int a = 5, b = 10;
cout << (a < b) << endl; // 1
cout << (a == b) << endl; // 0
| 操作符 | 含义 | 示例 |
|---|---|---|
== | 等于 | a == b |
!= | 不等于 | a != b |
< | 小于 | a < b |
> | 大于 | a > b |
<= | 小于等于 | a <= b |
>= | 大于等于 | a >= b |
五、逻辑操作符(Logical Operators)
逻辑操作符用于布尔表达式组合,返回 true 或 false。
| 操作符 | 含义 | 示例 | 特性 |
|---|---|---|---|
&& | 逻辑与 | (x > 0 && y > 0) | 短路:若左侧为假,右侧不求值 |
|| | 逻辑或 | (x < 0 || y < 0) | 短路:若左侧为真,右侧不求值 |
! | 逻辑非 | !flag | 取反 |
短路求值(short-circuit evaluation)是逻辑运算的重要特性,它不仅提升性能,也避免非法运算(例如除零)。
if (ptr != nullptr && *ptr > 10)
cout << "安全访问";
六、位操作符(Bitwise Operators)
位操作符直接作用于整数的二进制表示。
| 操作符 | 含义 | 示例 | ||
|---|---|---|---|---|
& | 按位与 | a & b | ||
| ` | ` | 按位或 | `a | b` |
^ | 按位异或 | a ^ b | ||
~ | 按位取反 | ~a | ||
<< | 左移 | a << n (相当于乘以 2ⁿ) | ||
>> | 右移 | a >> n (相当于除以 2ⁿ) |
示例:
unsigned int a = 5; // 00000101
unsigned int b = 3; // 00000011
cout << (a & b); // 00000001 -> 1
cout << (a | b); // 00000111 -> 7
cout << (a ^ b); // 00000110 -> 6
cout << (~a); // 11111010 -> 取反
注意:
- 位移运算的右操作数必须为非负;
- 有符号右移的高位填充由实现定义;
- 通常使用无符号类型进行位操作以避免歧义。
七、赋值与复合赋值操作符(Assignment Operators)
1. 基本赋值
int x = 10;
2. 复合赋值
| 操作符 | 等价形式 | ||
|---|---|---|---|
+= | a = a + b | ||
-= | a = a - b | ||
*= | a = a * b | ||
/= | a = a / b | ||
%= | a = a % b | ||
&= | a = a & b | ||
| ` | =` | `a = a | b` |
^= | a = a ^ b | ||
<<= | a = a << b | ||
>>= | a = a >> b |
复合赋值的优点是只计算一次左值,因此在复杂左值表达式中效率更高:
arr[getIndex()] += 10; // getIndex() 只调用一次
八、条件运算符(Ternary Operator)
三目运算符语法:
条件 ? 表达式1 : 表达式2;
示例:
int max = (a > b) ? a : b;
等价于:
if (a > b)
max = a;
else
max = b;
注意:
- 运算符返回一个值,因此可嵌入表达式;
- 但应避免多层嵌套,影响可读性。
九、逗号运算符(Comma Operator)
逗号运算符从左到右依次求值,整个表达式的结果是最后一个表达式的值:
int a = (b = 3, b + 2); // b=3执行后,再计算b+2,a=5
在 for 循环中常见:
for (int i = 0, j = 10; i < j; ++i, --j)
cout << i << " " << j << endl;
十、成员与指针操作符(Member & Pointer Operators)
| 操作符 | 含义 | 示例 |
|---|---|---|
. | 访问对象成员 | obj.member |
-> | 访问指针所指对象的成员 | ptr->member |
.* | 访问对象的成员指针 | (obj.*ptrMember) |
->* | 通过指针访问成员指针 | (ptr->*ptrMember) |
示例:
struct Test { int x; void show() { cout << x; } };
Test t{42};
Test* p = &t;
cout << t.x; // 使用 .
cout << p->x; // 使用 ->
十一、指针相关操作符
| 操作符 | 作用 | 示例 |
|---|---|---|
& | 取地址 | int* p = &x; |
* | 解引用 | cout << *p; |
这两个符号在声明与使用中含义不同:
int* p; // 声明指针类型
*p = 10; // 解引用赋值
十二、类型转换操作符(Cast Operators)
1. C 风格强制类型转换
int a = 10;
double b = (double)a;
2. C++ 风格强制类型转换
| 操作符 | 用途 |
|---|---|
static_cast<T>(expr) | 编译期已知的安全类型转换 |
dynamic_cast<T>(expr) | 用于多态类型的安全向下转换(运行期检查) |
const_cast<T>(expr) | 去除或添加 const 属性 |
reinterpret_cast<T>(expr) | 强制重解释类型(高风险) |
示例:
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 安全类型检查
十三、其他特殊操作符
1. sizeof
返回对象或类型的字节大小,结果类型为 size_t:
cout << sizeof(int); // 通常为4
对数组时返回整个数组的大小,而不是指针大小:
int arr[10];
cout << sizeof(arr); // 40 (假设int为4字节)
2. typeid
用于在运行时获取类型信息,常与 RTTI(运行时类型识别)配合使用。
#include <typeinfo>
int a = 5;
cout << typeid(a).name(); // 输出类型名
3. new 与 delete
动态内存分配与释放:
int* p = new int(10);
delete p;
int* arr = new int[5];
delete[] arr;
new 会自动调用构造函数,而 delete 调用析构函数。
十四、运算符优先级与结合性
| 优先级(高→低) | 操作符 | 结合方向 |
|---|---|---|
| 1 | :: | 左到右 |
| 2 | () [] -> . ++ -- | 左到右 |
| 3 | ! ~ ++ -- + - (type) * & sizeof new delete | 右到左 |
| 4 | * / % | 左到右 |
| 5 | + - | 左到右 |
| 6 | << >> | 左到右 |
| 7 | < <= > >= | 左到右 |
| 8 | == != | 左到右 |
| 9 | & | 左到右 |
| 10 | ^ | 左到右 |
| 11 | | | 左到右 |
| 12 | && | 左到右 |
| 13 | || | 左到右 |
| 14 | ?: | 右到左 |
| 15 | =, +=, -=, *=, /=, %= 等 | 右到左 |
| 16 | , | 左到右 |
建议使用括号明确运算顺序,尤其在混合表达式中。
十五、运算符重载(Operator Overloading)
C++ 允许为自定义类型定义运算符行为。例如:
class Vector {
public:
int x, y;
Vector(int x, int y): x(x), y(y) {}
Vector operator+(const Vector& v) const {
return Vector(x + v.x, y + v.y);
}
};
使用:
Vector a(1, 2), b(3, 4);
Vector c = a + b; // 等价于 a.operator+(b)
注意:
- 不能重载
.、::、?:、sizeof; - 运算符重载不会改变操作符优先级;
- 应保证语义一致性(例如重载
==时应与!=保持逻辑对称)。