本书封皮如下:
终于开始看这本书了,打算在2011年的最后一个月把这本书看完,相比之前看到一半看不下去的<C++程序设计语言>而已,这本书确实适合入门,那本书还是等到这本书看完再看吧。
wchar_t 是字符型 16位
unsigned 数值超出范围要取模326%256
C++中所有字符串面值在末尾加一个空字符,例如'A'与"A"不同,"A"为两个字符
初始化不是赋值,赋值是擦除当前值使用新值代替
若声明有初始化,那么它可被当做是定义,加上extern关键字并不影响,例如
extern double pi=3.14
要使const变量能够在其他文件中访问,显示时指定为extern
const引用可以绑定不同值相关类型的对象,或绑定到右值(非const不行)
编译器在实践中不会有任何存储空间,用于存储用常量表达式初始化的const常量
p75
当进行string对象和字符串字面值混合连接操作时,“+”操作符的左右操作数必须至少有一个string类型,例如
string s1="hello"+"," ;//wrong string s2=s1+"," ;//right string s3="hello"+","+s2 ;//wrong
p76
string.size操作结果变量为string.size_type类型,不能赋值给一个int类型变量
p79 vector初始化方式
vector<T> v1; //vector保存类型为T的对象,默认构造函数v1为空 vector<T> v2(v1); //v2是v1的一个副本 vector<T> v3(n,i); //v3包含n个值为i的元素 vector<T> v4(n); //v4含有值初始化的元素的n个副本
p82 下标操作部添加元素
vector<int> ivec; for(vector<int>::size_type ix=0;ix!=0;++ix) ivec[ix]=ix;
以上代码并没有给vector中添加任何元素,添加元素需要使用ivec.push_back(ix)
p84 迭代器
迭代器中end操作返回的迭代器指向vector的“末端元素的下一个”,通常称为超出末端迭代器,表明它指向了一个不存在的元素,如果vector为空,begin迭代器与end返回的迭代器相同,end返回的迭代器不能对它进行解引用或自增操作
p87 const_iterator
const_iterator对象可以用于const vector或非const vector,一旦它被初始化,只能用它来改写其指向的元素,但不能使它指向任何其他元素,例如
const vector<int> nines(10,9); const vector<int>::interator cit2=nines.begin(); //cit2可以改变所指向元素的值,worong vector<int>::const_iterator it=nines.begin(); //ok,it无法改变元素的值 *it=10;//*it 是const,wrong ++it;// it不是const,故可以改变
//一个无法写元素的迭代器 vector<int>::const_iterator //一个无法修改元素的迭代器 const vector<int>::iterator
p88 bitset初始化方法
bitset<n> b; //b有n位,每位都为0 bitset<n> b(u);//b是unsigned long型u的一个副本 bitset<n> b(s);// b是string对象s中含有的位串的副本 bitset<n> b(s,pos,n);//b是s中从位置pos开始的n个位的副本
p105 指针和引用比较
第一个区别在于引用总是指向某个对象,定义引用时没有初始化是错误的;第二个区别是赋值行为的差异:给引用赋值修改的是该引用所关联的对象的值,而不是使引用与另一个对象关联,引用一经初始化,就始终指向同一个特定对象。例如
int ival=1024,ival2=2048; int *pi=&ival,*pi2=&ival2; pi=pi2;
上面修改的是pi的值
int &ri=ival,&ri2=ival2; ri=ri2;
上面把ival2的值复制给ival,两个对象的值相等
p112 指针和typedef
typedef string *pstring; const pstring cstr;
以上声明const pstring时,const修饰的是pstring类型,这是一个指针,声明语句应该是把cstr定义为指向string类型的const指针,等价于:
string *const cstr;
而不是
const string *cstr;
如果这样来看
pstring const cstr=&s;//把const放在类型pstring之后,然后从右向左阅读该声明语句,cstr是const pstring类型
p115 strlen
strlen将会寻找到null为止,返回这一段内存空间中总共有多少字符,不包括结束符将会导致错误,例如:
char ca[]={'c','+','+'} cout<<strlen(ca)<<endl;
p123-124 指针和多维数组
定义指向多维数组的指针时,千万别忘了该指针所指向的多维数组其实是数组的数组。例如
int ia[3][4]; int (*ip)[4]=ia; ip=&ia[2]; //该指针指向四个int类型
使用typedef可以简化这样的指针,例如
typedef int int_array[4]; int_array *ip=ia; for(int_array *p=ia;p!=ia+3;++p) for(int *q=*p;q!=*p+4;++q) cout<<*q<<endl;
p131 符号
当只有一个操作数为负数时,求模操作的结果值的符号可依据分子或分母的符号而定。如果求模的结果随分子的符号,则除出来的值向零一侧取整,如果求模与分母的符号匹配,则除出来的值向负无穷一侧取整。除法操作的值是负数。其余两个操作数都为正或者都为负,则除法恒正,求模都相应为正,相应为负。
p141 在单个表达式中组合使用解引用和自增操作
vector<int>::iterator iter=ivec.begin(); while(iter!=ivec.end()) cout<<*iter++<<endl;
由于后增操作优先级高于解引用操作,因此*iter++等效于*(iter++),先使iter加1,然后返回iter原值的副本作为该表达式的结果,解引用操作的操作数为iter未加1前的副本。
p149 顺序
C++中规定操作数计算顺序的操作符有 ‘||’ ‘&&’ ‘?:’ 和 ‘,’ 除此之外其他操作符并未指定其操作数的求值顺序,例如
f1()*f2() //无法得知到底是先调用f1还是先调用f2
只有当操作符的两个操作数涉及到同一个对象,并改变其值时,操作数的计算次序才会影响结果,例如
if(ia[index++]<ia[index]) //有两种解释 if(ia[0]<ia[0]) if(ia[0]<ia[1])
p153 删除const对象
尽管程序员不能改变const对象的值,但可撤销对象本身,const动态对象使用删除指针来加以释放
const int *pci=new const int[1024]; delete pci; //删除成功
p159 const_cast
只有用const_cast才能将const的性质转换掉,例如
const char *pc_str; char *pc=string_copy(const_cast<char*>(pc_str));
p203 利用const引用避免复制
在向函数传递大型对象时,需要使用引用形参,使用引用形参,函数可以直接访问实参对象,而无须复制它。例如
bool isShorter(const string &s1,const string &s2) { return s1.size()<s2.size(); }
应该将不需要修改的引用形参定义为const引用,普通的非const引用形参在使用时不太灵活。这样的形参既不能用const对象初始化,也不能用字面值或产生右值的表达式实参初始化。
p207 形参
编译器忽略为任何数组形参指定的长度,例如
void printValues(const int ia[10]) { for(size_t i=0;i!=10;++i) { cout<<ia[i]<<endl; } }
尽管上述代码假定所传递的数组至少含有10个元素,但C++没有任何机制强制这个假设
int main() { int i=0,j[2]={0,1}; printValues(&i);//传递合法,但运行时出错,没有10个元素 printValues(j);//传递合法,运行时出错,没有10个元素 return 0; }
当编译器检查数组形参关联的实参时,它只会检查实参是不是指针、指针的类型和数组元素的类型是否匹配,而不会检查数组的长度。
p212 函数返回类型
返回类型为void的函数通常不能使用带返回值的return语句,只能用不带返回值的return表示函数强制结束。但是它可以返回另一个返回类型同样是void的函数的调用结果,例如
void do_swap(int &v1,int &v2) { int tmp=v2; v2=v1; v1=tmp; } void swap(int &v1,int &v2) { if(v1==v2) return false;//带返回值错误 return do_swap(v1,v2);//正确 }
p229 函数重载与重复声明的区别
如果两个函数声明的返回类型和形参表完全匹配,则将第二个函数声明视为第一个的重复声明,如果两个函数形参表完全相同,但返回类型不同,则第二个声明错误,例如
Record lookup(const Account&); bool lookup(const Account&);// 错误 typedef Phone Telno; Record lookup(Phone); Record lookup(const Phone); //重复声明
既可将const对象传递给const形参,也可传递给非const形参,此时两种形参等价。但形参与const形参等价性仅限于非引用形参,const引用形参和非const引用形参不同。如果函数带有指向const类型的指针形参,则与带有指向相同类型的非const对象的指针形参的函数不相同,例如
Record lookup(Account&); Record lookup(const Account&); const Account a(0); Account b; lookup(a); //调用lookup(const Account&) lookup(b); //调用lookup(Account&)
不能基于指针本身是否为const来实现函数的重载
f(int *); f(int * const);//重复声明,const用于修饰指针本身,而不是修饰指针所指向的类型,指针本身是否为const没有带来区别
p237 指向函数的指针
可以使用typedef简化函数指针的定义
typedef bool (*cmpFcn)(const string &,const string &);
在引用函数名但又没有调用该函数时,函数名将自动解释为指向函数的指针。例如:
bool lengthCompare(const string &,const string &);
对其任何使用都解释为如下类型的指针
bool (*)(const string &,const string &);
可使用函数名对函数指针做初始化或赋值,函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化赋值
cmpFcn pf1=0; cmpFcn pf2=lengthCompare; //等价于 comFcn pf2=&lengthCompare pf1=lengthCompare; pf2=pf1;
p238 指向返回函数的指针
int (*ff(int)) (int * ,int);
ff(int)为一个函数,带有一个int类型的形参,该函数返回 int(*)(int *,int),这是一个指向函数的指针,改写为:
typedef int (*PF)(int *,int); //PF为一个指向函数的指针 PF ff(int);
允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数
typedef int func(int *,int);//func为一个函数类型 void f1(func); //正确,直接调用该函数 func f2(int); //错误,返回的是函数 func *f3(int) ; //正确,返回指向函数的指针
p251-252 文件流的使用
ifstream infile(ifile.c_str()); //infile是读的流 ofstream outfile(ofile.c_str()); //outfile是写的流 ifstream infile; //infile为读文件的流对象 ofstream outfile; //outfile为写文件的流对象,使用之前需要绑定 infile.open("in"); //与特定对象绑定 outfile.open("out"); if(!infile) { cerr<<"error:can not open"<<ifile<<endl; return -1; }
p256 使用流打开文件
ifstream& open_file(ifstream &in,const string &file) { in.close(); //如果已经打开则先关闭 in.clear(); //清除已经存在的错误 in.open(file.c_str());//打开文件 return in; }
p265 容器初始化函数
C<T> c; //创建一个名为c的容器,C是容器类型,T是元素类型,适用于所有容器 C c(c2) ; //创建容器c2的副本c,c和c2必须具有相同的容器类型,并存放相同类型的元素,适用于所有容器 C c(b,e);//创建c,其元素是迭代器b和e标示的范围内元素的副本,适用于所有容器 C c(n,t);// 用n个值为t的元素创建容器c,其中t值必须是容器类型C的元素类型的值,或者可转换为该类型的值,仅用于顺序容器 C c(n);//创建有n个值初始化元素的容器c,仅用于顺序容器
p270 迭代器范围
有两个迭代器用于标记容器中的一段元素范围,命名为begin/end,或者first/last,第二个迭代器last,end指向最后元素的下一个元素,如果两个迭代器相等,则迭代器范围为空。
迭代器元素范围属于左闭合区间,即[first, last),意义为
1、当first和last相等时,迭代器范围为空
2、当first与last不等时,迭代器范围内至少有一个元素,而且first指向该区间中的第一个元素。
p276 避免存储end操作返回的迭代器
vector<int>::iterator first=v.begin(), last=v.end(); while(first!=last) { first=v.insert(first,42); ++first; }
上述代码行为未定义,将导致死循环,end返回的迭代器存储在last局部变量中,循环进行元素的添加,添加元素会使得存储在last中的迭代器失效。该迭代器既没有指向容器v的元素,也不再指向v的超出末端的下一位置
不要存储end操作返回的迭代器,添加删除deque或vector容器内的元素都会导致存储的迭代器失效。可以把上述代码修改如下:
while(first!=v.end())
{...}
p310 map类型
map的value_type是存储元素的键以及值的pair类型,而且键为const,例如value_type可以为
pair<const string, int> //值成员可以修改,但键成员不可修改
p356 五种迭代器
输入迭代器:读,不能写,只支持自增运算。只能顺序使用,一旦输入迭代器增加,就无法再使用它之前的元素
输出迭代器:写,不能读,只支持自增运算。要求每个迭代器的值必须正好写入一次
前向迭代器:读和写,只支持自增运算。只会以一个方向遍历序列,支持对同一个元素的多次读写
双向迭代器:读和写,支持自增和自减运算。从两个方向读写容器
随机访问迭代器:读和写,支持完整的迭代器算术运算。在常量时间内访问容器任意位置。
p359 泛型算法形参模式
alg(beg,end,other parm); alg(beg,end,dest,other parm); //dest用于指定存储输出数据的目标对象 alg(beg,end,beg2,other parm); //beg2用于指定第二个输入范围 alg(beg,end,beg2,end2,other parm); //beg2,end2指定第二个输入范围
beg和end指定算法操作的元素范围,称为输入范围,dest,beg2,end2都是迭代器
先写到这里,剩下的第三、四、五部分还未看完,到时候放在PartB中了。