新站整站排名优化火速公司seo网站页面优化包含
一、基础
- 表达式由一个或多个运算对象组成。字面值和变量是最简单的表达式。
- 一元运算符、二元运算符、三元运算符,分别由一个、两个、三个运算对象
- 对于含有多个运算对象的复杂表达式,要理解运算符的优先级、结合率和运算对象的求值顺序。如果优先级不同,则先算优先级高的运算;如果优先级相同,则按照结合律确定如何计算。在大多数情况下,不会明确指定求值顺序。括号无视优先级和结合律
/*有四种运算符规定嘞运算对象的求值顺序*&& //先求左侧运算对象的值,只有当左侧运算对象为真时才会继续求右侧运算对象的值*|| //先求左侧运算对象的值,只有当左侧运算对象为假时才会继续求右侧运算对象的值*?: //*, //从左到右*//*int i = f1() * f2(); //如果f1和f2之间相互影响,那么求值顺序会影响i的结果*std::cout << i << " " << ++i << endl; //先求++i,还是先输出,对输出结果有影响*/*两条经验规则:*拿不准优先级、结合律时,使用括号强制让表达式的组合关系符合程序逻辑要求*如果改变了运算对象的值,在表达式的其它地方不要再使用这个运算对象。当然,*++iter这种情况除外*/
- 在表达式求值时,运算对象的类型也许并不相同,此时要转化成同一种类型,转换规则有哪些?
- 重载运算符时,运算对象的类型和返回值类型可以自定义,但是运算对象个数、运算符优先级和结合率是无法改变的
- 右值是取不到地址的表达式;左值是能取到地址的表达式。
- 一个对象被用作右值时,用的是对象的值;一个对象被用作左值时,用的是对象的身份(内存中的位置)。当一个左值被当成右值时,实际上使用的是它的内同。
- 内置类型和迭代器的递增递减运算符作用域左值运算对象,前置所得的也是左值,而后置所得是右值(可以理解为得到的一个变化前的副本)
- 对于int *p,decltype(&P)得到的是int**,是左值;decltype(*P)得到的是int&,是左值。
二、运算符
2.1、算数运算符
/*算术运算符包括:* +(一元正号)、-(一元负号)** *、/、%** +(加号)、-(减号)**算术运算符是从左向右结合*加、减可以运用于指针,加减单位为指针指向的数据单元的类型所占的空间大小*//*算术表达式可能产生未定义结果:*Ⅰ、数学性质本身,例如除0操作*Ⅱ、计算机表示溢出*//*取模操作,两个运算对象必须是整数,如何理解有的运算对象是负数的情况呢?*如果m、n皆为整数,并且n非0时,满足(m/n)*n + m%n即可*-21%-8 = -5; -21/-8 = 2;*-21%8 = -5; -21/8 = -2;*/
2.2、逻辑和关系运算符
/*逻辑与关系元素运算符包括:(除!外,均为从左向右结合)*!,逻辑非, !expr**<,小于, expr1 < expr2*<=,小于等于, expr1 <= expr*>,大于, expr1 > expr2*>=,大于等于, expr1 >= expr**==,等于, expr1 == expr2*!=,不等于, expr1 != expr2* *&&,逻辑与, expr1 && expr2*||,逻辑或, expr1 || expr2*//*逻辑与和逻辑或,用来判断若干条件是否同时成立\部分成立*逻辑非,会将运算对象的值取反后返回*关系元素符,会返回布尔值,所以几个运算符连写在一起会产生意想不到的结果*进行比较时,除非比较的对象时布尔类型,否则尽量不用true和false与之比较*/
2.3、赋值运算符
/*赋值运算符的左侧运算对象必须是一个可修改的左值*若赋值运算符的左右两个运算对象类型不一致,则右侧运算对象转化为左侧运算对象的类型*C++11允许使用花括号括起来的初始值列表作为右侧的运算对象,此时,如果存在窄化转化,将会报错*赋值运算符满足从右向左的运算顺序,对于多重赋值语句,左侧类型要么与右侧一致,要么可有右侧转化得到*复合运算符:对对象施以某种运算,然后把计算结果再赋给该对象*+=、-=、*=、/=、%=、<<=、>>=、&=、^=、|=均为复合运算符*/int k{3.12}; //错误,浮点数的表达范围比int要大,窄化转换 int val, *pval; val = pval = 0; //错误,pval是int*,val是int,类型不同并且int无法通过int*转化得到 string s1, s2; s1 = s2 = "ok"; //正确,虽然ok是字面值常量,但是可以转化成string
2.4、递增和递减运算符
/*递增和递减分为前置和后置两种:*前置:对对象进行自增或自减之后,返回对象,可以作为左值*后置,对对象进行自增或自减,返回对象修改之前的副本,是右值*在不需要用到修改前的值时,选用前置,可以减少副本的开销*/vector<int> vec; …… //往vec中加入一些对象 auto pbeg = vec.begin(); while (pbeg != v.end() && *beg >= 0)std::cout << *pbeg++ << std::endl; /*后置递增优先级高于解引用,故而*pbeg++相当于*(pbeg++)*如果使用前置,那么无法输出第一个元素,而且如果vec中没有负值,程序可能试图访问一个不存在的元素*/
2.5、成员访问运算符
/*点运算符和箭头运算符均可用于访问成员*箭头运算符作用于一个指针类型的对算对象,结果是左值*点运算符的成员所属的对象是左值,则结果为左值,否则为右值*/struct Point {int x;int y; };Point getPoint() {return Point{1, 2}; // 返回一个临时对象(右值) }int a = getPoint().x; // getPoint().x 是右值,因为 getPoint() 返回的是右值 getPoint().x = 10; // 错误:getPoint().x 是右值,不能赋值
2.6、条件运算符
/*条件运算符的(?:)允许我们把简单的if-else逻辑嵌入单个表达式*嵌套条件运算符中,条件运算符是从右向左开始结合,所以靠右的条件运算构成靠左条件运算的:分支*条件运算符优先级非常低,通常在其两侧加上括号*/std::cout << ((grade < 60) ? "fail" : "pass") << std::endl; //正确 std::cout << (grade < 60) ? "fail" : "pass"; //输出0或者1 std::cout << grade < 60 ? "fail" : "pass" << std::endl; //错误,试图比较cout和60
2.7、位运算符
/*位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合*位运算提供检查和设置二进制位的功能*//*位运算符包括:满足左结合率*~,位取反 ~expr**<<,左移 expr1 << expr2*>>,右移 expr1 >> expr2**&,位与 expr1 & expr2**^,位异或 expr1 ^ expr2* *|,位或 expr1 | expr2*//*关于符号位的处理没有明确规定,因此建议仅将位运算符用于处理无符号数*若运算对象为“小整型”,则其值会被自动提升成int型*在移位运算符中,右侧的数必须严格小于结果的位数,否则将产生未定义行为*移位运算符的优先级比算术运算符优先级低,比关系、赋值、条件运算符高*/unsigned long qiuz = 0; //将长整型数置为0 quiz != 1UL << 27; //将quiz的位置27置为1 quiz &= ~(1UL << 27); //将quiz的位置27置为0 bool flag = quiz & (1UL << 27) //得到quiz的位置27的状态
2.8、sizeof运算符
/*sizeof运算符的两种形式:*Ⅰ、sizeof(type),即sizeof(类型),要求某个类型所占空间大小时,一定要用括号括住类型名,否则会报错*Ⅱ、sizeof expr,即sizeof(表达式)**sizeof运算符的结果:*Ⅰ、对char或者char类型表达式执行sizeof运算,结果为1*Ⅱ、对引用类型执行sizeof,得到被引用类型所占空间的大小*Ⅲ、对指针执行sizeof,返回指针所占空间的大小*Ⅳ、对解引用指针执行sizeof,得到指针指向的对象所占空间大小,指针不需要有效*Ⅴ、对数组执行sizeof,不会把数组转换成指针进行处理,得到数组所有元素所占的空间大小*Ⅵ、对string和vector进行sizeof,没有太大意义,不能得到对象中元素占用的空间大小*/int arr[3]; int *p; int &ref = arr[0]; sizeof(arr); //大小为3个int型大小 sizeof(*p); //大小为1个int型大小 sizeof(p); //大小为1个int*型大小 sizeof(ref); //大小为1个int型大小
2.9、逗号运算符
- 逗号运算符含有两个运算对象,按照从左到右一次求值,逗号运算符的最终结果是右侧表达式的值
2.10、类型转换
- 隐式类型转化
/*隐式类型转换:*Ⅰ、在大多数表达式中,比int型小的整型值首先提升为较大的整型*Ⅱ、在条件中,非布尔值转化为布尔值*Ⅲ、初始化和赋值时,右侧对象转换成左侧运算对象的类型*Ⅳ、若算数运算或关系运算的运算对象有多种类型时,需要转化成一种类型,尽量不损失精度*Ⅴ、函数调用时,设计的类型转换*//*算术转换:*运算符的运算对象将转换成最宽的类型*Ⅰ、对于bool、char、signed char、unsigned char、short、unsigned short,转换成较大的整型*Ⅱ、一个对象是无符号,一个是有符号,如果无符号类型不小于有符号类型,则将带符号类型转化成无符号*Ⅲ、如果有符号类型大于无符号,如果无符号类型的所有值均可存于有符号类型,则无->有,否则有->无*/ 3.14159L + 'a'; //'a'被提升为int,然后int转化为long double dval + ival; //ival转化为double dval + fval; //fval转化为double ival = dval' //dval转化成int flag = dval; //dval转化成bool cval + fval; //cval提升为int,int转化为float sval + cval; //两者均被提升为int cval + lval; //cval被转化为long ival + ulval; //ival被转化为unsigned long usval + ival; //根据unsigned short和int所占空间大小进行转化 uival + lval; //根据unsigned int和long所占空间大小进行转化/*Ⅰ、数组转化成指针:大多数情况下,数组自动转化成指向数组首元素的指针*数组被当作decltype的参数,作为取地址符、sizeof以及typeid等的运算对象时,不转化成指针*Ⅱ、指针转化:0和nullptr可以转换成任意指针类型,任意类型的非常量指针能转化成void*,任意类型指针可以转化成const void* *Ⅲ、类类型定义的转化,如while(cin >> s) {},istream类型的值被转化成布尔值*/
- 显示转化
/*强制类型转化形式:cast-name<type>(expression);*其中cast-name有static-cast、dynaic-cast、const-cast、reinterpret-cast四种*//*static-cast:任何具有明确定义的类型转化,只要不包含底层const,均可使用该方式*/ int i, j; double dval = static_cast<double>(j) / i; void *p = &dval; //正确,任何非常量对象的地址都能存入void* double *dp = static_cast<double*>(p); //正确,将void*转化回初始的指针类型/*const-cast:只能改变运算对象底层的const*/ const char *pc; char *p = const-cast<char*>(pc); //正确,但是通过p写值是未定义行为 string *ps = const-cast<string>(pc); //错误,const-cast只能改变常量属性/*reinterpret-cast:为运算对象的位模式提供较底层次上的重新解释*/ 不建议使用,不学了,啊哈哈哈哈哈/*能不进行强制类型转化就不进行强制类型转换*//*旧时的强制类型转化:*Ⅰ、type (expr)*Ⅱ、(type) expr*/
2.11、运算符优先级
太多了,不记了,不知道咋办就用括号
提示:
- void*是一种无类型指针,编译器无法知道它指向的具体数据类型,无法直接解引用