表达式 = 操作数 + 操作符
表达式的结果一般为右值,可以读取该结果值,但是不允许对它进行赋值;
对于操作数为内置或复合类型的二元操作符,通常要求它的两个操作数具有相同的数据类型,或者其类型可以转换为同一种数据类型,比如int型可以转换为double类型;
算数操作符
左结合
关系逻辑操作符
逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。称为“短路求值”;
不能串接使用关系操作符;
位操作符
对于为操作符,由于系统不能确保如何处理其操作数的符号位,所以建议使用unsigned整型操作数
赋值操作符
赋值操作符的左操作数必须是非const的左值;
右结合性;
自增和自减操作符
int i = 0, j; j = i++; //j = 0, i = 1 j = ++i; //j = 2, i = 2 //前置操作返回加1后的值,所以返回对象本身,这是左值。而后置操作返回的则是右值当我们希望在单个复合表达式中使用变量的当前值,然后再加1,通常使用后置++;
在单个表达式中组合使用解引用和自增操作,可简化代码
vector<int>::iterator iter = ivec.begin(); while (iter != ivec.end()) cout<< *iter++ <<endl; //由于后自增操作的优先级高于解引用操作,*iter++等价于*(iter++),即先时iter加1,然后返回iter原值的副本作为该表达式的结果,因此,解引用操作的操作数式iter未加1前的副本操作符优先级
复合表达式的处理原则:
如果有怀疑,则在表达式上按程序逻辑顺序要求使用圆括号强制操作数的组合;
如果要修改操作数的值,则不要在同一个语句的其他地方使用该操作数。如果必须使用改变的值,则把该表达式分割成两个独立语句:在一个语句中改变该操作数的值,再在下一个语句使用它
new 和 delete表达式
动态创建对象得分默认初始化:
string *ps = new string(); //初始化为空字符串 int *pi = new int; //未初始化 int *pi = new int(); //初始化为0值初始化的()语法必须置于类型名的后面,而不是变量后;
动态创建的对象用完后,必须显式地将该对象占用的内存返回给自由存储区;
如果指针指向的不是new分配的内存地址,则在该指针上使用delete是不合法的;但如果指针的值是0,则是合法的;
一旦删除了指针所指向的对象,立即将指针置为0,这样就非常清楚地表明指针不再指向任何对象。
tips:三种常见的与动态内存分配有关的错误:
删除指向动态分配内存的指针失败,因为无法将该块内存返还给自由存储区。称为“内存泄漏”,一般很难发现,只有等到程序运行一段时间后,耗尽了所有内存空间时,内存泄漏才会显露出来。读写已删除的对象。对同一个内存空间使用两次delete操作,自由存储区可能被破坏。类型转换
隐式类型转换
如果赋值操作的左右操作数类型不相同,则右操作数会被转换为左边的类型
int val = 3.14; //double类型的3.14被编译器自动转换为int类型的3 //在混合类型的表达式中,其操作数被转换为相同的类型 int ival; double dval; ival >= dval; //ival转换为double //用作条件的表达式被转换为bool类型 int ival; if(ival) //ival转换为bool which(cin) //cin转换为bool //用一表达式初始化某个变量,或将一表达式赋给某个变量,该表达式被转换为该变量的类型 int ival = 3.14 + 3;算术转换
转换规则定义了一个类型转换层次,该层次规定了操作数应按什么次序转换为表达式中最宽的类型
bool flag; char cval; short sval; unsigned short usval; int ival; unsigned int uival; long lval; unsigned long ulval; float fval; unsigned float ufval; 3.14159L + 'a'; //'a'->int->double dval + ival; //ival->double dval + fval; //fval->double ival = dval; //dval->int 截断 flag = dval; //if dval is 0,then flag is false,otherwise true cval + fval; //cval->int->float sval + cval; //sval and cval ->int cval + lval; //cval->long ival + ulval; //ival->unsigned long usval + ival; //取决于unsigned 和 int的大小 uival + lval; //取决于unsigned int和long的大小指针转换
使用数组时,一般数组会自动转换为指向第一个元素的指针;例外:对数组取地址(&)或sizeof或数组对数组的引用进行初始化时,不会将数组转换为指针;
指向任意类型的指针都可转换为viod*类型;整型数值常量0可转换为任意指针类型;
转换为bool类型
算术值和指针值都可以转换为bool类型;bool类型也可转换为int型 false–0 true–1;
转换为const对象
int i; const int ci = 0; const int &j = i; //ok 将非const转换为const const int *p = &ci; //ok 将非const对象的地址(或指针)转换为指向相关const类型的指针显式(强制)类型转换 cast
cast-name (表达式)
dynamic_cast
支持运行时识别指针或引用所指向的对象,可将基类类型的指针或引用安全地转换为派生类型的指针或引用
const_cast
转换掉表达式的const性质
static_cast
将一个较大的算术类型赋给较小的类型时有用,强制转换的结果应与原来的地址值相等
reinterpret_cast
通常为操作数的位模式提供较低层次的重新解释
tips:避免使用强制类型转换,如果非强制转换不可,则应限制强制转换值得作用域,并记录所有假定涉及的类型,这样能减少错误的发生