C++ Primer中文版(第四版)-读书笔记【PartA】

本书封皮如下:

C++ Primer中文版(第四版)-读书笔记【PartA】_第1张图片

 

终于开始看这本书了,打算在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中了。

你可能感兴趣的:(C++,String,vector,读书,iterator,存储)