1.运算符重载时,我们可以重新定义已存在运算符的行为,但是无法修改运算对象的个数、运算符的优先级和结合律。
2.在C++中存在左值(lvalue)和右值(rvalue)的区别:左值是地址,可以出现在赋值语句的左边和右边(当它出现在右边时,实际上使用的是它的值);右值是变量的值,只可以出现在赋值语句的右边。c++11中新增的decltype作用于得到左值的表达式(注意不是变量)时返回的是引用类型,而decltype作用于右值时得到的是普通的类型。
int a, b=10; a = 5; //a左值,5右值 a = b; //a左值,b左值(转化为右值10) decltype(a) c; //int型,a是变量 decltype((a)) d; //int &型,(a)是一个表达式,返回a左值3.除了&&(逻辑与)、||(逻辑或)、? :(问号运算符)和 ,(逗号运算符)之外,c++中其他运算符没有规定运算对象的求值顺序,所以如果一个子表达式修改了另外一个子表达式的变量,那么运算结果无法预计,应当予以避免。
int i = 0; cout<<i<<" "<<++i<<endl; //i和++i不确定谁先执行,输出"0 1"和"1 1"都有可能4.c++11中改进了结果为负数的商取整的方式,规定一律向0取整。同时也规定了当%(求模)的两个操作数一正一负时的行为,让它等于两个绝对值求模之后再加上一个负号。
int d = -20.1/3; //d向0取整,等于-6 d = -20%-3; //都是负数,得到-2 d = -20%3; //等价于d=-(20%3),得到-25.赋值操作符是右结合的,而算术运算符是左结合的。例如:
a+b+c 就是(a+b)+c 而a=b=c 就是a=(b=c)6.列表赋值:c++11中允许使用{}括起来的初始值列表作为赋值语句的右侧运算对象。(注意内置类型列表初始化不能损失精度)
int k = {3.14}; //错误,精度损失 vector<int> v; v = {1,2,3}; //ok7.具有求值顺序的操作符:
8.对于内置类型来说,前置递增++递减--,与后置递增++递减--,在只执行变量自增1或者自减1的运行效率上是一致的。但要注意在对复杂迭代器的处理上,后置++或者--效率要比前置版本低很多。所以,如非必要,在使用迭代器时,推荐使用前置版本。
9.在bitmap或者simhash中,我们要用到位操作,下面列一下常用位操作:
result |= 1<<x; //原数字与0..010..0或,利用或的特性保留其他位的值,只将第x位置1
result &= ~(1<<x); //原数字与1..101..1与,利用与的特性保留其他位的值,只将第x位设置为0;对0..010..0求反刚好得到1..101..1
status= result & (1<<x); //原数字与0..010..0与,利用与的特性去掉其他位的值,只保留第x位
int cnt = 0; while(result) { result &= result - 1; ++cnt; }
10.关于左移<<和右移>>,这里再扯个淡。c++语法规范中规定了,当右操作数大于做操作数的bit数时,运算结果是无法预测的。在intel处理器的SHL和SHR中,左移右移的计数器(counter)只有5bit(32位)或者6bit(64位),当你左移或者右移大于32或者64时,会造成计数器溢出,得到意料之外的结果。
int operand_r = 32; unsigned int i = 1 << operand_r; //因为处理器的左移计数器溢出,所以相当于:1<<0,i等于1,而不是unsigned int溢出之后的0
11.sizeof操作符返回size_t类型的常量表达式(所以可以作为数组的维度),它并不实际计算运算对象的值(与decltype很像啊)。所以sizeof一个无效指针(未初始化或者已被delete)的解引用是安全的,能够返回正确的值。sizeof以一个byte作为1,对象占用几个byte就返回几。在c++11中,允许sizeof直接作用于类的成员,而不需要实现实例化一个类的对象。
char c; sizeof c; //返回1,一个byte嘛 char &rc = c; sizeof rc; //返回被引用值的大小,1 char *p; sizeof p; //返回指针的大小,一般为一个int,4 sizeof *p; //返回被指向对象的大小,为初始化也没关系,1 char ca[16] = {}; sizeof ca; //返回数组的大小,16 string s; sizeof s; //返回固定部分大小,4 vector v; sizeof v; //返回固定部分大小,12 sizeof myclass::member; //c++11新增,方便了 int ia[16] = {0}; size_t length = sizeof(ia)/sizeof(*ia); //length = 16,利用sizeof特性计算数组长度12.内部算数类型隐式转换规则:
13.其他类型的隐式转换:
int i = 0; while(i!=10); //错误的空语句,导致死循环 ++i; if (i > 9); //错误的空语句,导致后续statment总是执行 cout<<i<<endl;
16.复合语句:使用{}包括起来的一段语句块,被视为一个语句,它内部的变量具有块作用域。所以可以使用块语句扩展if/else/while/for/do等等后面的statement语句,达到多做很多事的目的。(原来还有这么深的道理,我一直以为if等语句后面跟这个{}是天然的呢.......汗!)
17.悬垂else:悬垂else是指,在同一语句作用域内,每个else都将与它最接近的if匹配,这有些时候会造成逻辑上的错误(与程序员预想不一致),在每个if和else之后使用{},划分清楚作用域即可解决此问题。
18.关于switch语句:switch后面的()中只能放置结果为整型的表达式;case后面只能紧跟常量表达式(constexpr出场);执行了一个case之后,如果没有break,C++将执行下一个case直到switch结束或者遇到break为止;只能在switch的最后(default或者最后一个case)中定义变量,如果想在这之前定义变量,请在该变量的外面添加{}表示语句块,否则当程序未执行到此case时,会导致这个变量在后面的语句中出现未定义的错误。
19.注意:在for语句头初始化的变量只在循环内部可见。
for(int i=0; i<10; i++) //do sth int k = i; //错误,i出了作用域,已被销毁20.c++11中引入了范围for语句(类似于Java中的for_each),来简化for循环的编写。
for (auto element : sequence) do sth
需要注意三点:
string s = "abcd"; for (auto &c : s) c = toupper(c);
vector<string> v = {"abcd","efgh"}; for (const auto &e : v) cout<<e;
bool dosth() bool dosth() { { thing *p = new thing(); thing *p = new thing(); bool bRet = true; bool bRet = true; do{ // 执行并进行错误处理 // 执行并进行错误处理 bRet = func1(); bRet = func1(); if(!bRet) goto errorhandle; if(!bRet) break; bRet = func2(); bRet = func2(); if(!bRet) goto errorhandle; if(!bRet) break; // processing....... // processing...... // 执行成功,释放资源并返回 // 执行成功,释放资源并返回 delete p; delete p; p = NULL; p = NULL; return true; return true; errorhandle: }while(0); delete p; delete p; p = NULL; p = NULL; return false; return false; } }
#define SAFE_DELETE(p) delete p; p = NULL; if(NULL != p) SAFE_DELETE(p) //逻辑错误,SAFE_DELETE只被执行了一半,另外一半被扔到了if外边,必定执行 //改成: #define SAFE_DELETE(p) {delete p; p = NULL} if(NULL != p) SAFE_DELETE(p); //语法错误,{}后面跟着;无法通过编译 //改成: #define SAFE_DELETE(p) do{ delete p; p = NULL} while(0) //就可以避免以上的错误,既不会被拆开后面多个;也能正确执行
22.break语句和continue语句与悬垂else具有同样的工作原理,与距离最近的while/for/switch等语句匹配,注意不要产生逻辑上的错误。
23.异常的处理过程:异常发生之后,首先在本函数中查找是否有匹配的catch语句,如果没有,那么终止本函数,向上在调用这个函数的函数中查找,如果还没有则终止调用的函数继续向上...如果一直找到顶层(main)都没有匹配的catch,则调用terminate函数终止整个程序。