操作符:
C++提供了丰富的操作符,并且支持操作符重载。标准库正式使用这些功能定义用于库类型的操作符。
表达式:
表达式由一个或者多个操作数通过操作符组合而成。可以简单到只有一个字面值常量或者变量,也可以由操作符以及一个或者多个操作数构成。
算术操作符:
按优先级降序第一组: +(正), -(负)
按优先级降序第二组: *, /, %
按优先级降序第三组: +(加), -(减)
%:
21%6=3;//正常情况的取模
-21%-6=-3//两个负数的模依然为负数
21%-6=?//具体值依赖于机器
关系操作符:
返回bool值
按优先级降序第一组:!
按优先级降序第二组:<, <=, >, >=
按优先级降序第三组:==, !=
按优先级降序第四组: &&
按优先级降序第五组: ||
短路求值(short-circuit evaluation):
逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。我们常常称这种求值策略为“短路求值short-circuit evaluation”。
比如:
expr1 && expr2 // logical AND
expr1 || expr2 // logical OR
仅当由 expr1 不能确定表达式的值时,才会求解 expr2。也就是说,当且仅当下列情况出现时,必须确保 expr2 是可以计算的:
1在逻辑与表达式中,expr1 的计算结果为 true。此时只有当 expr2 的值也是 true时 ,逻辑与表达式的值才为 true。
2在逻辑或表达式中,expr1 的计算结果为 false。如果 expr1 的值为 false,则逻辑或表达式的值取决于 expr2 的值是否为 true。
逻辑与操作符的用法要点:
对于逻辑与操作符,一个很有价值的用法是:如果某边界条件使 expr2 的计算变得危险,则应在该条件出现之前,先让 expr1 的计算结果为 false。例如,编写程序使用一个 string 类型的对象存储一个句子,然后将该句子的第一个单词的各字符全部变成大写,可如下实现:
string s("Expressions in C++ are composed...");
string::iterator it = s.begin();
// convert first word in s to uppercase
while (it != s.end() && !isspace(*it)) {
*it = toupper(*it); // toupper covered in section 3.2.4 (p. 88)
++it;
}
条件是否为true的判断:
If(val){ /*do something*/}//只要val为任意非零值,条件判断都得true。
位操作符:
按优先级降序第一组:~位求反
按优先级降序第二组:<<左移, >>右移
按优先级降序第三组:&位与
按优先级降序第四组:^位异或
按优先级降序第五组: |位或
位操作与bitset相互转化:
假设某老师带了一个班,班中有 30 个学生,每个星期在班上做一次测验,只有及格和不及格两种测验成绩,对每个学生用一个二进制位来记录一次测试及格或不及格,以方便我们跟踪每次测验的结果,这样就可以用一个bitset对象或整数值来代表一次测验:
bitset<30> bitset_quiz1; // bitset solution
unsigned long int_quiz1 = 0; // simulated collection of bits()
老师可以设置和检查每个位。例如,假设第27位所表示的学生及格了,则可以使用下面的语句适当地设置对应的位:
bitset_quiz1.set(27);
int_quiz1 |= 1UL<<27; (本句等价于int_quiz1 = int_quiz1 | 1UL << 27;)
如果老师重新复核测验成绩,发现第 27 个学生实际上在该次测验中不及格,这时老师应把第 27 位设置为 0:
bitset_quiz1.reset(27); // student number 27 failed
int_quiz1 &= ~(1UL<<27); // student number 27 failed
最后,可通过以下代码获知第 27 个学生是否及格:
bool status;
status = bitset_quiz1[27]; // how did student number 27 do?
status = int_quiz1 & (1UL<<27); // how did student number 27 do?
赋值操作符:
在循环中,可以进行赋值操作符相关的化简。
int i = get_value(); // get_value returns an int
while (i != 42)
{
// do something ...
i = get_value();
}
可以被化简为
int i;
while ((i = get_value()) != 42)
{
// do something ...
}
自增和自减操作符的前置和后置形式:
这两种操作符的后置形式同样对其操作数加 1(或减 1),但操作后产生操作数原来的、未修改的值作为表达式的结果。
而前置操作返回加1后的值,所以返回对象本身,是左值。而后置操作返回的则是右值。
int i = 0, j;
j = ++i; // j = 1, i = 1: prefix yields incremented value
j = i++; // j = 1, i = 2: postfix yields unincremented value
建议:多使用前置操作符,少使用后置操作符,以减少程序开销
在单个表达式中组合使用解引用和自增操作:
vector<int>::iterator iter = ivec.begin();
// prints 10 9 8 ... 1
while (iter != ivec.end())
cout << *iter++ << endl; // iterator postfix increment
由于后自增操作的优先级高于解引用操作,因此 *iter++ 等效于 *(iter++)。子表达式 iter++ 使 iter 加 1,然后返回 iter 原值的副本作为该表达式的结果。因此,解引用操作 * 的操作数是 iter 未加 1 前的副本。
sizeof操作符:
对数组做 sizeof 操作等效于将对其元素类型做 sizeof 操作的结果乘上数组元素的个数。
因为 sizeof 返回整个数组在内存中的存储长度,所以用 sizeof 数组的结果除以 sizeof 其元素类型的结果,即可求出数组元素的个数:
// sizeof(ia)/sizeof(*ia) returns the number of elements in ia
int sz = sizeof(ia)/sizeof(*ia);
逗号操作符:
逗号表达式是一组由逗号分隔的表达式,这些表达式从左向右计算。逗号表达式的结果是其最右边表达式的值。如果最右边的操作数是左值,则逗号表达式的值也是左值。此类表达式通常用于for循环:
int cnt = ivec.size();
// add elements from size... 1 to ivec
for(vector<int>::size_type ix = 0;
ix != ivec.size(); ++ix, --cnt)
ivec[ix] = cnt;
复合表达式的求值:
操作数的分组结合方式取决于操作符的优先级和结合性。
各个操作符的优先级和结合性。
New和delete表达式:
new 和 delete 表达式可以动态创建和释放数组,这两种表达式也可用于动态创建和释放单个对象。
定义变量时,必须指定其数据类型和名字。而动态创建对象时,只需指定其数据类型,而不必为该对象命名。取而代之的是,new 表达式返回指向新创建对象的指针,我们通过该指针来访问此对象:
int i; // named, uninitialized int variable
int *pi = new int; // pi points to dynamically allocated,
// unnamed, uninitialized int
这个 new 表达式在自由存储区中分配创建了一个整型对象,并返回此对象的地址,并用该地址初始化指针 pi。
如果不提供显式初始化,动态创建的对象与在函数内定义的变量初始化方式相同(第 2.3.4 节)。对于类类型的对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化。
string *ps = new string; // initialized to empty string
int *pi = new int; // pi points to an uninitialized int
delete pi; //
动态创建的对象用完后,程序员必须显式地将该对象占用的内存返回给自由存储区。C++ 提供了 delete 表达式释放指针所指向的地址空间。
一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。