目录
5.1 算术操作符
5.2 关系操作符和逻辑操作符
5.3 位操作符
5.4 赋值操作符
5.5 自增和自减操作符
5.6 箭头操作符
5.7 条件操作符
5.8 sizeof操作符
5.9 逗号操作符
5.10 复合表达式的求值
5.11 new和delete表达式
5.12 类型转换
表达式由一个或多个操作数通过操作符组合而成,每个表达式都会产生一个结果。
5.1 算术操作符
算术操作符(优先级相同时,操作符从左向右结合)
| 操作符 | 功能 |
| + | 一元正号 |
| - | 一元负号 |
| * | 乘法 |
| / | 除法 |
| % | 求余 |
| + | 加法 |
| - | 减法 |
%只能用于整数,包括bool,char,short,int和long类型,以及对应的unsigned类型。
5.2 关系操作符和逻辑操作符
| ! | 逻辑非 | < | 小于 |
| <= | 小于等于 | > | 大于 |
| >= | 大于等于 | == | 相等 |
| != | 不等 | && | 逻辑与 |
| || | 逻辑或 |
1. 逻辑与、逻辑或操作符
短路求值;如果某边界条件使expr2的计算变得危险,则就应在该条件出现之前,先让expr1的计算结果为false。
2. 逻辑非操作符
3. 不应该串接使用关系操作符
4. 相等测试与bool字面值
直接用变量作为条件进行检测(可以转换为bool类型)
5.3 位操作符
位操作符使用整型的操作数。位操作符将其整型操作数视为二进制位的集合,为每一位提供检验和设置功能,还可用于bitset类型。
| ~ | 位求反 | << | 左移 |
| >> | 右移 | & | 位与 |
| ^ | 位异或 | | | 位或 |
对于位操作符,系统不能确保如何处理其操作数的符号位,因些强烈建议使用unsigned整数。
>>和<<操作符提供移位操作,其右操作数标志要移动的位数;左移操作符<<在右边插入0以补充空位;对于右移操作符>>,如果其操作数是无符号数,则从左边开始插入0;如果是有符号数,则插入符号位的副本或者0值。移位操作符的右操作数不可为负。
位与操作符&需要两个整型操作数,在每个位的位置,如果两个操作数对应的位都为1,则操作数结果中该位为1,否则为0。
位异或操作符^需要两个整型操作数,在每个位的位置,只有一个位为1,则操作数结果中该位为1,否则为0。
位或操作符|需要两个整型操作数,在每个位的位置,有一个或两个位为1,则操作数结果中该位为1,否则为0。
5.3.1 bitset对象或整型值的使用
设置第27位为1,其他为0。
bitset<30> bitset_quizl; //bitset solution unsigned long int_quizl = 0; //simulated collection of bits bitset_quizl.set(27); //inside student number 27 passed int_quizl |= 1UL << 27;
重新设置第27位为0;
bitset_quizl.reset(27); int_quizl &= -(1UL << 27);
5.3.2 将移位操作符用于IO
IO操作符为左结合。
5.4 赋值操作符
赋值操作符的左操作数必须是非const的左值;赋值表达式的值是其左操作数的值,其结果的类型为左操作数的类型。
5.4.1 赋值操作的右结合性
ival = jval = 0; //从右向左结合
5.4.2 赋值操作具有低优先级
谨防混淆相等操作符和赋值操作符。
5.4.3 复合赋值操作符
a op = b; //op 可以是以下操作符之一: +=, -=, *=, /=, %=, <<=, >>=, |=, ^=
5.5 自增和自减操作符
只有必要时才使用后置操作符(前置操作符效率更高)。
1. 后置操作符返回未加1的值。
2. 在单个表达式中组合使用解引用和自增操作
*iter++; //解引用操作*的操作九是iter未加1前的副本。
5.6 箭头操作符
点操作符用于获取类类型对象的成员;如果有一个指向Sales_item对象的指针(或迭代器),则在使用点操作符前,需对该指针(或)迭代器进行解引用。
(*sp).Sales_item(item2); //注意不要丢掉()
当然可以用->代替*
sp->.Sales_item(item2); //就不用()了
5.7 条件操作符
唯一的三元操作符。
cond ? exp1 : exp2;
1. 避免条件操作符的深度嵌套
int max = i > j ? i > k ? i : k : j > k ? j :k; //--> int max = i; if (j > max) max = j; if (k > max) max = k;
2. 在输出表达式中使用条件操作符
条件操作符的优先级相当低,如果有一个更强大的表达式嵌入条件表达式时,通常必须用圆括号把条件表达式括起来。
5.8 sizeof操作符
sizeof操作符的作用是返回一个对象或类型名的长度,返回值的类型为size_t,长度的单位是字节。
sizeof (type name); sizeof (expr);//将sizeof应用在表达式expr上,将获得该表达式的结果的类型长度 sizeof expr;//the same as above
将sizeof用于expr时,并没有计算表达式expr的值。特别是在sizeof *p中,指针p可以持有一个无效地址,因为不需要对p做解引用操作。
使用sizeof的结果部分地依赖所涉及的类型:
(1) 对char类型或值为char类型的表达式做sizeof操作保证得1。
(2) 对引用类型做sizeof操作将返回存放此引用类型对象所需的内存空间大小。
(3) 对指针做sizeof操作将返回存放指针所需的内存大小;注意,如果要获取该指针所指向对象的大小,则必须对该指针进行解引用。
(4) 对数组做sizeof操作等效于将对其元素类型做sizeof操作的结果乘上数组元素的个数。
5.9 逗号操作符
从左向右计算,结果是其最右边的表达式。
5.10 复合表达式的求值
5.10.1 优先级
5.10.2 结合性
赋值操作有右结合性,这个特性允许将多个赋值操作串接起来。
ival = jval = kval = lval
表 5.4. 操作符的优先级
|
Associativity and Operator 操作符及其结合性 |
Function 功能 |
Use 用法 |
See Page 参见页码 |
|
|---|---|---|---|---|
|
L |
:: |
global scope(全局作用域) |
:: name |
p. 450 |
|
L |
:: |
class scope(类作用域) |
class :: name |
p. 85 |
|
L |
:: |
namespace scope(名字空间作用域) |
namespace :: name |
p. 78 |
|
L |
. |
member selectors(成员选择) |
object . member |
p. 25 |
|
L |
-> |
member selectors(成员选择) |
pointer -> member |
p. 164 |
|
L |
[] |
subscript(下标) |
variable [ expr ] |
p. 113 |
|
L |
() |
function call(函数调用) |
name (expr_list) |
p. 25 |
|
L |
() |
type construction(类型构造) |
type (expr_list) |
p. 460 |
|
R |
++ |
postfix increment(后自增操作) |
lvalue++ |
p. 162 |
|
R |
-- |
postfix decrement(后自减操作) |
lvalue-- |
p. 162 |
|
R |
typeid |
type ID(类型 ID) |
typeid (type) |
p. 775 |
|
R |
typeid |
run-time type ID(运行时类型 ID) |
typeid (expr) |
p. 775 |
|
R |
explicit cast(显式强制类型转换) |
type conversion(类型转换) |
cast_name <type>(expr) |
p. 183 |
|
R |
sizeof |
size of object(对象的大小) |
sizeof expr |
p. 167 |
|
R |
sizeof |
size of type(类型的大小) |
sizeof(type) |
p. 167 |
|
R |
++ |
prefix increment(前自增操作) |
++ lvalue |
p. 162 |
|
R |
-- |
prefix decrement(前自减操作) |
-- lvalue |
p. 162 |
|
R |
~ |
bitwise NOT(位求反) |
~expr |
p. 154 |
|
R |
! |
logical NOT(逻辑非) |
!expr |
p. 152 |
|
R |
- |
unary minus(一元负号) |
-expr |
p. 150 |
|
R |
+ |
unary plus(一元正号) |
+expr |
p. 150 |
|
R |
* |
dereference(解引用) |
*expr |
p. 119 |
|
R |
& |
address-of(取地址) |
&expr |
p. 115 |
|
R |
() |
type conversion(类型转换) |
(type) expr |
p. 186 |
|
R |
new |
allocate object(创建对象) |
new type |
p. 174 |
|
R |
delete |
deallocate object(释放对象) |
delete expr |
p. 176 |
|
R |
delete[] |
deallocate array(释放数组) |
delete[] expr |
p. 137 |
|
L |
->* |
ptr to member select(指向成员操作的指针) |
ptr ->* ptr_to_member |
p. 783 |
|
L |
.* |
ptr to member select(指向成员操作的指针) |
obj .*ptr_to_member |
p. 783 |
|
L |
* |
multiply(乘法) |
expr * expr |
p. 149 |
|
L |
/ |
divide(除法) |
expr / expr |
p. 149 |
|
L |
% |
modulo (remainder)(求模(求余)) |
expr % expr |
p. 149 |
|
L |
+ |
add(加法) |
expr + expr |
p. 149 |
|
L |
- |
subtract(减法) |
expr - expr |
p. 149 |
|
L |
<< |
bitwise shift left(位左移) |
expr << expr |
p. 154 |
|
L |
>> |
bitwise shift right(位右移) |
expr >> expr |
p. 154 |
|
L |
< |
less than(小于) |
expr < expr |
p. 152 |
|
L |
<= |
less than or equal(小于或等于) |
expr <= expr |
p. 152 |
|
L |
> |
greater than(大于) |
expr > expr |
p. 152 |
|
L |
>= |
greater than or equal(大于或等于) |
expr >= expr |
p. 152 |
|
L |
== |
equality(相等) |
expr == expr |
p. 152 |
|
L |
!= |
inequality(不等) |
expr != expr |
p. 152 |
|
L |
& |
bitwise AND(位与) |
expr & expr |
p. 154 |
|
L |
^ |
bitwise XOR() |
expr ^ expr |
p. 154 |
|
L |
| |
bitwise OR(位异或) |
expr | expr |
p. 154 |
|
L |
&& |
logical AND(逻辑与) |
expr && expr |
p. 152 |
|
L |
|| |
logical OR(逻辑或) |
expr || expr |
p. 152 |
|
R |
?: |
conditional(条件操作) |
expr ? expr : expr |
p. 165 |
|
R |
= |
assignment(赋值操作) |
lvalue = expr |
p. 159 |
|
R |
*=, /=, %=, |
compound assign(复合赋值操作) |
lvalue += expr, etc. |
p. 159 |
|
R |
+=, -=, |
p. 159 |
||
|
R |
<<=, >>=, |
p. 159 |
||
|
R |
&=,|=, ^= |
p. 159 |
||
|
R |
throw |
throw exception(抛出异常) |
throw expr |
p. 216 |
|
L |
, |
comma(逗号) |
expr , expr |
p. 168 |
5.10.3 求值顺序
复合表达式的处理指导原则:(1) 如果有怀疑,则在表达式上按程序逻辑要求使用圆括号强制操作数的组合。(2)如果要修改操作数的值,则不要在同一语句的其他地方使用该操作数。如果必须使用改变的值,则把该表达式分割成两个独立的语句:在一个语句中改变该操作数的值,再在下一个语句中使用它。(3)一个表达式里,不要在两个或更多的子表达式中对同一对象做自增或自减操作。
5.11 new和delete表达式
1. 动态创建对象的初始化
动态创建的对象可用初始化变量的方式实现初始化:
int *pi = new int(1024); //object to which pi points is 1024 string *ps = new string(10, '9'); //*ps is "9999999999"
2. 动态创建对象的默认初始化
如果不提供显式初始化,动态创建的对象与在函数内定义的变量初始化方式相同,对于类类型的对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化。对于提供了默认构造函数的类类型,没有必要对其对象进行值初始化(会自动调用)。而对于内置类型或没有定义默认构造函数的类型,采用不同初始化方式则有显著的差别:
int *pi = new int; //pi points to an uninitialized int int *pi = new int(); //pi points to an int value-initialized to 0
3. 耗尽内存
如果new表达式无法获取需要的内存空间,系统将抛出名为bad_alloc的异常。
4. 撤销动态创建的对象
delete 指针
5. 零值指针的删除
安全。
6. 在delete之后,重设指针的值
一旦删除了指针所指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。
7. const对象的动态分配和回收
动态创建const对象:
const int *pci = new const int(1024);
动态创建的const对象必须在创建时初始化,并且一经初始化,其值就不能修改。对于类类型的const动态对象,如果该类提供了默认的构造函数,则此对象可隐式初始化。内置类型或未提供默认构造函数的类类型对象必须显式初始化。
8. 删除const对象
与普通对象的释放方式一样。
5.12 类型转换
C++定义了算术类型之间的内置转换尽可能防止精度损失。
5.12.1 何时发生隐式类型转换
在混合类型的表达式中,其操作数被转换为相同的类型;用作条件的表达式被转换为bool类型;用一表达式初始化某个变量,或一表达式赋值给某个变量,则该表达式被转换为该变量的类型。
5.12.2 算术转换
整型提升:所有比int小的整型,包括char、signed char、unsigned char、short和unsigned short,如果该类型的所有可能的值都能包容在int内,它们就会被提升为int型,否则将被提升为unsigned int。
1. 有符号和无符号类型之间的转换
包含short和int类型的表达式,short->int;如果int能容纳unsigned short,则unsigned short->int,否则unsigned short,int->unsigned int。同理,unsigned int->long,否则unsigned int,long->unsigned long。
对于包含signed和unsigned int型的表达式,其转换有副作用,尽量不要使用。
2. 理解算术转换
5.12.3 其他隐式转换
1. 指针转换
数组:
int ia[10]; int *ip = ia;
不将数组转换为指针的情况有:数组用作取地址(&)操作符的操作数或sizeof操作符的操作数时,或用数组对数组的引用进行初始化时。
2. 转换为bool类型
0、null或空字符转换为false。
3. 算术类型与bool类型的转换
将bool对象转换为算术类型时,true变成1,而false则为0。
4. 转换与枚举类型
C++自动将枚举转换为整型。
5. 转换为const对象
当使用非const对象初始化const对象的引用时,系统将非const对象转换为const对象,还可以将非const对象的地址(或非const指针)转换为指向相关const类型的指针:
int i; const int cj = 0; const int &j = i; const int *p = &ci;
6. 由标准库类型定义的转换
5.12.4 显式转换
static_cast,dynamic_cast,const_cast,reinterpret_cast。
5.12.5 何时需要强制类型转换
用于强制覆盖通常的标准转换。
5.12.6 命名的强制类型转换
cast-name<type>(expression); /* cast-name = static_cast,dynamic_cast,
const_cast,reinterpret_cast */
1. dynamic_cast
2. const_cast
将转换掉表达式的const性质。
3. static_cast
编译器隐式执行的任何类型转换都可以由static_cast显式完成。
4. reinterpret_cast
通常为操作数的位模式提供较低层次的重新解释。
int *ip; char *pc = reinterpret_cast<char*>(ip);
尽量避免使用强制类型转换。
5.12.7 旧式强制类型转换
type (expr); (type) expr;