C++_Primer04.expression
表达式
优先级
| 运算符 | 功能 | 用法例 |
|---|---|---|
| :: | 全局作用域 | ::name |
| :: | 类作用域 | class::name |
| :: | 命名空间作用域 | namespace::name |
| . | 成员选择 | object.member |
| -> | 成员选择 | pointer->member |
| [] | 下标 | expr[expr] |
| () | 函数调用 | name(expr_list) |
| () | 类型构造 | type(expr_list) |
| ++ | 后置递增运算 | lvalue++ |
| -- | 后置递减运算 | lvalue-- |
| typeid | 类型ID | typeid(type) |
| typeid | 运行时类型ID | typeid(expr) |
| explicit cast | 类型转换 | cast_name |
| ++ | 前置递增运算 | ++lvalue |
| -- | 前置递减运算 | --lvalue |
| ` | 位求反(反引号) | `expr |
| ! | 逻辑非 | !expr |
| - | 一元负号 | -expr |
| + | 一元正号 | +expr |
| * | 解引用 | *expr |
| & | 取地址 | &lvalue |
| () | 类型转换 | (type)expr |
| sizeof | 对象的大小 | sizeof expr |
| sizeof | 对象的大小 | sizeof(type) |
| sizeof... | 参数包的大小 | sizeof...(name) |
| new | 创建对象 | new type |
| new[] | 创建数组 | new type[size] |
| delete | 释放对象 | delete expr |
| delete[] | 释放数组 | delete[] expr |
| noexcept | 能否抛出异常 | noexcept(expr) |
| ->* | 指向成员选择的指针 | ptr->*ptr_to_member |
| .* | 指向成员选择的指针 | obj.*ptr_to_member |
| * | 乘法 | expr * expr |
| / | 除法 | expr / expr |
| % | 取余 | expr % expr |
| + | 加号 | |
| - | 减号 | |
| << | 向左移位(包括重载后的IO运算符) | |
| >> | 向右移位 | |
| < | 小于 | |
| <= | ||
| > | ||
| >= | ||
| == | ||
| != | ||
| & | 位与 | |
| ^ | 位异或 | |
| | | 位或 | |
| && | 逻辑与(短路运算) | |
| || | 逻辑或(短路运算) | |
| ? : | 条件(三目运算符) | |
| = | 赋值 | |
| *=, /+, %= | ||
| +=, -= | ||
| <<=, >>= | ||
| &=, |=, ^= | ||
| throw | 抛出异常 | |
| , | 逗号 | expr, expr |
表达式中的括号忽略优先级,优先计算括号内的内容
C++ 允许使用赋值运算作为条件:if(i = j)
赋值运算的结果为值本身,如果 j 不为0,则条件将为真
除非必要,否则不使用递增递减运算符的后置版本
因为相比前置版本,后置版本需要将原始值存储下来以便返回这个未修改的值
如果我们不需要修改前的值,那么后置版本是一种浪费
混用解引用和递增运算符
cout << *iter++ << endl;
// 等价于
cout << *iter << endl;
++iter;
这种类型的混用比较常见
但是如果一条语句中用了多次同一个引用,则其行为是未定义的:
*beg = toupper(*beg++);
// 编译器可能按照下面任意一种思路处理:
*beg = toupper(*beg);
*(beg+1) = toupper(*beg);
显式类型转换
cast-name
其中 cast-name 可以是:static_cast, dynamic_cast, const_cast 和 reinterpret_cast
static_cast
可以把一个较大的算术类型赋值给较小的类型,并且会忽略精度损失。
一般如果编译器发现一个较大的算术类型试图赋值给较小的类型会给出警告信息;但当执行了显式类型转换后,警告信息就会被关闭了。
// 整型转换成浮点型
double slope = static_cast<double>(j) / i;
// 指针类型转换
void *p = &d;
double *dp = static_cast<double*>(p);
const_cast
cast away the const,去掉 const 性质
一旦去掉了某个对象的 const 性质,编译器就不会再组织我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的;而如果对象是一个常量,再使用 const_cast 执行写操作就会产生未定义的后果。
只有 const_cast 能改变表达式的常量属性,其他形式的强制类型转换改变表达式的常量属性都将引发编译器错误。同样,也不能使用 const_cast 改变表达式的类型。
const char *cp;
string s = static_cast<string>(cp); // 使用 static_cast 改变类型
char *p = const_cast<char*>(cp); // 使用 const_cast 改变const属性
const char* p1 = const_cast<const char*>(p);
reinterpret_cast
为运算对象的位模式提供较低层次上的重新解释,比如把 int 型数据转换成 char 型
int *ip;
char *pc = reinterpret_cast<char*>(ip);
此时我们必须记住 pc 指向的真是对象是一个 int 而非字符,比如把 pc 当做普通字符指针使用就可能在运行时发生错误:
string str(pc);
reinterpret_cast 本质上依赖于机器,想要安全使用它就必须对涉及的类型和编译器实现转换的过程都非常了解
应尽量避免使用强制类型转换,尤其是 reinterpret_cast
在有重载函数的上下文中使用 const_cast 无可厚非,但在其他情况下使用就意味着程序存在某种设计缺陷
旧式强制类型转换
type (expr); // 函数形式的强制类型转换
(type) expr; // C 语言风格的强制类型转换
根据类型不同,旧式强制类型转换分别与 const_cast,static_cast 或 reinterpret_cast 相似的行为。
如果换成 const_cast 和 static_cast 也合法,则其行为与对应的强制类型转换一致。如果替换后不合法,则与 reinterpret_cast 功能类似:
int *ip = &i;
char *pc = (char*) ip;

浙公网安备 33010602011771号