C++ Primer阅读心得(第四章、第五章)
来源:互联网 发布:mysql 导入表 编辑:程序博客网 时间:2024/04/30 12:22
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取整,等于-6d = -20%-3; //都是负数,得到-2d = -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.具有求值顺序的操作符:
- &&(逻辑与):短路规则,只有在左侧为true时才对右侧求值;这个运算符的运算对象和返回值都是右值。
- ||(逻辑或):短路规则,只有在左侧为false时才对右侧求值;这个运算符的运算对象和返回值都是右值。
- ?:(条件运算符):先求条件表达式,再根据结果对后面的两个表达式之一进行求值;根据 : 两侧的表达式来决定返回值类型:都是左值,返回左值,否则返回右值。
- , (逗号运算符):先求左边再求右边;它返回右侧表达式的计算结果(右侧返回左值则返回左值,反之返回右值)。
8.对于内置类型来说,前置递增++递减--,与后置递增++递减--,在只执行变量自增1或者自减1的运行效率上是一致的。但要注意在对复杂迭代器的处理上,后置++或者--效率要比前置版本低很多。所以,如非必要,在使用迭代器时,推荐使用前置版本。
9.在bitmap或者simhash中,我们要用到位操作,下面列一下常用位操作:
- 某位置1:
result |= 1<<x; //原数字与0..010..0或,利用或的特性保留其他位的值,只将第x位置1
- 某位置0:
result &= ~(1<<x); //原数字与1..101..1与,利用与的特性保留其他位的值,只将第x位设置为0;对0..010..0求反刚好得到1..101..1
- 检查某位:
status= result & (1<<x); //原数字与0..010..0与,利用与的特性去掉其他位的值,只保留第x位
- 计算1的总数:
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; //返回被引用值的大小,1char *p; sizeof p; //返回指针的大小,一般为一个int,4sizeof *p; //返回被指向对象的大小,为初始化也没关系,1char ca[16] = {};sizeof ca; //返回数组的大小,16string s;sizeof s; //返回固定部分大小,4vector v;sizeof v; //返回固定部分大小,12sizeof myclass::member; //c++11新增,方便了int ia[16] = {0};size_t length = sizeof(ia)/sizeof(*ia); //length = 16,利用sizeof特性计算数组长度12.内部算数类型隐式转换规则:
- 对于整型:char/signed char/unsigned char/short/int 先被提升为int;如果unsigned short能被int装下,则unsigned short也被转换为int,否则unsigned short和int都被转换为unsigned int;如果表达式中包含unsigned int型,则表达式被提升为unsigned int型,如果表达式中包含long型,则表达式被提升为long型,如果表达式中有unsigned long,则表达式被提升为unsigned long型;如果unsigned int能被long装下,则unsigned int也会被提升为long型,否则unsigned int和long都会被提升为unsigned long型。
- 对于浮点型:如果表达式中包含float型,则表达式被提升为float型,如果表达式中包含double型,则表达式被提升为double型。
13.其他类型的隐式转换:
- 数组名转换为首元素的指针
- 0或者nullptr转换为任意类型的指针;任意类型的指针转换为void *类型
- 非空指针或者非零算术值转换为true,空指针或者零算术值转换为false
- 非常量指针或者引用转换为常量指针或者引用(反之则不行)
- 类类型的自动转换(需要重载操作符)
- static_cast:静态转换,相当于c中的类型转换,编译过程中转换不成功将报错
- dynamic_cast:动态转换,具有运行时类型检查的特性,只能被使用在将变量转化为类的指针/类的引用/void*时,进行父类子类之间的现实转换时更加安全
- const_cast:去掉变量的const/volatile属性
- reinterpret_cast:重解释转换,可以在任何两种类型之间进行转换,而不必担心编译器的检查,由程序员自己负起转换后正确使用的责任
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 sthint k = i; //错误,i出了作用域,已被销毁20.c++11中引入了范围for语句(类似于Java中的for_each),来简化for循环的编写。
for (auto element : sequence) do sth
需要注意三点:
- 可以在范围for中声明引用,然后使用此引用来修改序列中的值。(好东西,比Java自由一些)
string s = "abcd";for (auto &c : s) c = toupper(c);
- 如果不声明为引用,那么语句中声明的临时变量是序列中元素的副本,修改它不会影响到序列本身。在序列的元素很占用空间的情况下,可以使用const &来实现只读加减少复制(节省空间)的效果。
vector<string> v = {"abcd","efgh"};for (const auto &e : v) cout<<e;
- 在范围for语句中,不要修改序列本身(新插入一个元素或者删除一个元素),因为这会导致范围for持有的序列end()迭代器失效,进而造成越界访问等问题。
- 替代goto语句:假设我们有个函数要分配一些资源,然后在中途执行过程中如果遇到错误则退出函数,在退出前先释放资源,那么可以用do while(0)代替goto。
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;} }
- 在定义了多条语句的宏定义中使用,防止宏定义被if/else/while等语句拆开:
#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函数终止整个程序。
- C++ Primer阅读心得(第四章、第五章)
- C++primer plus(第四版)第五章习题参考
- C++primer plus阅读笔记第四章
- 《C++primer》第五版 第四章 第五章 笔记
- C++ Primer阅读心得(第三章)
- C++ Primer阅读心得(第六章)
- C++ Primer阅读心得(第七章)
- C++ Primer阅读心得(第十二章)
- C++ primer阅读心得(第十三章)
- C++ Primer阅读心得(第十四章)
- C++ Primer阅读心得(第十五章)
- C++ Primer阅读心得(第十六章)
- 【重学《C++Primer第四版》】第五章、表达式
- C primer plus(第五版)编程练习第四章
- 《C++primer(第五版)》学习之路-第四章:表达式
- C++primer第五版第四章学习笔记
- C++Primer 中文版 第五版 第四章课后习题答案
- C++primer 第五章
- 今天同事问的一个简单的C51问题
- SVM的发展和研究热点
- 穷学生的爱情
- 一些目录索引
- just a test
- C++ Primer阅读心得(第四章、第五章)
- Lucene开发环境配置;Lucene开发包中Demo调试。
- 搬家
- 使 MSN Messenger 7.5 支持 Windows 2000 的方法
- 序列化,反序列化时低序位非打印 ASCII 字符的问题
- 不同生命,同样的浪漫
- 上弦月,金桂飘香
- 友谊
- Delphi 中把文本文件装入资源文件中