C++Primer学习笔记概要Word下载.docx
《C++Primer学习笔记概要Word下载.docx》由会员分享,可在线阅读,更多相关《C++Primer学习笔记概要Word下载.docx(15页珍藏版)》请在冰豆网上搜索。
5.const_iteratorvsconst的iterator:
const_iterator是指迭代器指向的内容的值不能改变,用来只读vector;
const的iterator是指迭代器不能改变,很少用到。
6.任何改变vector长度的操作都会使已存在的迭代器失效。
例如,再调用push_bach之后,就不能再信赖指向vector的迭代器的值了。
7.string、vector、bitset是由标准库提供的类型,所以他们都有相应的方法;
C++提供的内置类型int、数组、指针等都没有提供方法。
第4章数组和指针
1.数组维数必须用值大于1的常量表达式定义,不允许数组直接复制和赋值。
2.指针保存0值,表明它不指向任何对象,所以删除指针后置0是个好办法。
3.C++提供了一种特殊的指针类型void*,它可以保存任何类型对象的地址。
void*指针只支持几种有限的操作:
与另一个指针进行比较;
向函数传递void*指针或者从函数返回void*指针;
给另一个void*指针赋值。
不允许使用void*指针操纵它所指向的对象。
4.指向const对象的指针和const指针:
指向const对象的指针(constint*pi)不能通过该指针修改指向对象的值;
const指针(int*constpi)指针本身不能变。
指针和引用:
引用是别名,必须在定义时初始化,且定义后不能再绑定到其他对象。
5.C风格字符串:
char数组,且以null结束。
charca[]={'
c'
'
+'
\o'
};
char*ca1=”primer”;
sizeof的时候会算上\0,strlen不会算上\0。
6.用new创建动态数组:
intsize=get_size();
int*p=newint[size];
用delete释放空间:
delete[]p;
([]必须,数组的释放不同一般指针的释放)
7.int*ip[4]:
声明了一个4维度的指针数组
int(*ip)[4]:
声明了一个指向数组的指针,可以迭代二维数组。
第5章表达式
1.sizeof操作符的作用是返回一个对象或类型名的长度,返回值的类型为size_t。
数组ia的元素个数:
intsize=sizeof(ia)/sizeof(*ia);
2.逗号表达式是一组由逗号分隔的表达式,这些表达式从左向右计算。
逗号表达式的结果是其最右边表达式的值。
3.类型转换(分隐式转换和显式转换)。
有四种强制类型转换符:
static_cast、dynamic_cast、const_cast、reinterpret_cast。
dynamic_cast操作符用于将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。
const_cast用于转换掉表达式的const性质。
static_cast能显式完成编译器隐式执行的任何类型转换。
reinterpret_cast通常为操作数的位模式提供较低层次的重新解释。
旧式强制转换:
type(expr)或(type)expr两种形式。
第6章语句
1.Switchcase:
case标号必须是整型常量表达式(case3.14:
和caseival:
就不行)。
在switch内部要定义常量,必须用块结构(防止影响其他case)。
2.标准异常类定义在四个头文件中:
i.exception头文件定义了最常见的异常类,它的类名是exception。
这个类只通知异常的产生,但不会提供更多的信息。
ii.stdexception头文件定义了几种常见的异常类:
runtime_error,range_error,overflow_error,underflow_error,logic_error,domain_error(参数的结果值不存在),invalid_argument,length_error,out_of_range
iii.new头文件定义了bad_alloc异常类型,提供因无法分配内存而由new抛出的异常。
iv.type_info头文件定义了bad_cast异常类型,dynamic_cast失败抛出的异常。
3.预处理器定义了四种在调试时非常有用的常量:
__FILE__文件名
__LINE__当前行号
__TIME__文件被编译的时间
__DATE__文件被编译的日期
第7章函数
1.形参的初始化与变量的初始化一样:
如果形参具有非引用类型,则复制实参的值;
如果形参为引用类型,则它只是实参的别名。
2.如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为const引用。
3.引用指针的形参:
voidptrswap(int*&
v1,int*&
v2)
4.c++程序员倾向与通过传递指向容器中需要处理的元素的迭代器来传递容器。
5.数组当形参时,非引用(起始是传递指针,能改变数组的内容)会忽略数组第一维。
通过引用传递数组:
voidprintValue(int(&
arr)[10])(括号是必须的)
6.理解返回引用至关重要的是:
千万不能返回局部变量的引用。
7.既可以在函数声明也可以在函数定义中指定默认实参。
但是,在一个文件中,只能为一个形参指定默认实参一次。
通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中。
8.内联函数应该在头文件中定义,这一点不同于其他函数。
在头文件中加入或修改内联函数时,使用了该头文件的所有源文件都必须重新编译。
9.指向函数的指针:
bool(*pf)(conststring&
conststing&
)(*pf两侧的圆括号是必需的)。
函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化或赋值。
如boollengthCompare(conststing&
conststring&
);
pf=lengthCompare;
调用时可以:
pf(“hi”,”bye”)或(*pf)(“hi”,”bye”)。
第8章标准IO库
1.I/O对象不可复制或赋值,故函数参数和返回值只能是I/O对象的指针或引用。
2.标准库提供了三种类型的流:
iostream,fstream,stringstream,多次读取时要注意流状态的清除。
3.刷新缓存区:
使用endl(插入换行符),flush(不添加任何字符),ends(插入null),使用unitbuf操作符(cout<
<
unitbuf<
”result”<
nounitbuf;
);
再就是把输入输出绑定在一起(cin.tie(&
cout))。
4.如果程序员需要重用文件流读写多个文件,必须在读另一个文件之前调用clear清除该流的状态。
(其他IO流也需要clear清除流状态)
5.字符串流stringstreasm可以提供字符串的转化和格式化。
const使用总结
1.const修饰变量,表示该变量为常量,必须在定义是初始化,之后不能修改它的值。
2.const修饰指针:
∙constint*ip=&
val:
ip是指向const对象(int)的指针,即不能通过(*ip)修改val的值(如果val不是const常量,可以通过其他方式修改val的值);
∙int*constip=&
ip是指向int对象的const指针,即不能修改ip的值,但(*ip)的值可以修改;
∙constint*constip=&
ip是指向const对象的const指针。
3.Const修饰函数:
constint&
op(constint)const
∙const修饰返回值,表示返回值不能修改,只有在返回引用类型时才有效(不能返回局部对象的引用);
∙const修饰参数,表示该参数不能在函数中修改。
需要注意的是参数是指针的情况,到底是const指针还是指向const对象的指针;
∙const修饰函数:
const修饰类的成员函数时,表示该函数不能修改类的成员变量。
4.
第9章顺序容器
1.关联容器和顺序容器的本质区别在于:
关联容器通过键(key)存储和读取元素,而顺序容器的元素排列次序与元素值无关,是由元素添加到容器里的次序决定。
2.标准库定义了三种顺序容器类型:
vector,list和deque。
vector是连续存储的,能支持快速随机访问;
list不需要连续存储,所以能在中间快速插入和删除;
deque是双端队列。
标准库还提供了三种容器适配器:
后进先出的stack、先进先出的queue和有优先级管理的priority_queue。
stack可以建立在vector、list、deque上,queue要能提供push_front操作,不能建立在vector上,priority_queue要求提供随机访问能力,只能建立在vector或deque上。
3.定义元素是容器的容器时,必须用空格隔开两个相邻的>
符号,以示这是两个分开的符号,否则,系统会认为>
>
是单个符号,为右移操作符,并结果导致编译时错误;
example:
vector<
vector<
string>
>
lines;
4.容器元素类型必须满足两个约束:
∙元素类型必须支持赋值运算
∙元素类型的对象必须可以复制
引用不支持一般意义的赋值运算,IO标准库类型不支持赋值和复制操作,所以不能创建存储他们的容器。
5.顺序容器上的操作
∙初始化(适用于关联容器)
i.C<
T>
c默认初始化,创建一个空的容器
ii.Cc(c2)创建容器c2的副本c,两个容器的类型和元素类型都必须相同
iii.Cc(b,e)创建c,其元素是迭代器b和c标示的范围内元素的副本,只要迭代器存储的值能转化为容器的元素即可,不需要容器类型相同。
iv.Cc(n,t)/Cc(n)创建有n个值为t(或默认值)的容器c,只适用于顺序容器。
∙向容器内添加元素
有三类函数push_back()、push_front()、insert(),用insert()在指定位置插入单个元素时,返回指向新元素的迭代器,其他的都返回void
∙容器大小操作
size(),max_size(),empty()resize()。
resize()能删除多出来的元素。
∙访问元素
用迭代器迭代容器或者使用下标操作(list不支持下标操作)
∙删除元素
erase()(返回指向删除元素后的迭代器),clear(),pop_back(),pop_front()(pop_操作只删除元素,不返回删除的元素值,返回void)
∙assign赋值操作,swap交换容器操作(不会破坏迭代器)
6.listvsvectordeque
∙list的元素不是连续存储,所以不支持随机访问,所以不支持下标操作([])和at()操作,其上的迭代器不支持+n操作和大小比较操作(只支持等于,不等操作)
∙vector不支持前端操作(push_front()pop_front())
7.修改容器时,会使容器上的迭代器失效。
vector和deque连续存储,在中间插入删除元素时,会重组织存储。
8.string类型不支持以栈方式操纵容器;
string支持的其他操作:
substrappendreplacefindrfindfind_frist_ofcompare。
第10章关联容器
1.标准库提供的关联容器有:
map,set,multimap,multiset。
map存储键值对,set存储单个键,multi支持同一个见多次出现。
2.pair类型和make_pair函数:
对map上的迭代器解引用是pair<
contT1,T2>
对象。
3.map、set上的键类型,唯一的约束就是必须支持<
操作符,至于是否支持其他的关系或相等运算,则不做要求。
4.map,set,multimap,multiset
∙都支持insert,count,find,erase操作以及lower_bound,upper_bound,equal_range操作(注意他们的返回值),初始化见顺序容器;
∙只有map支持下标操作,而且与下标访问数组或vector的行为截然不同:
用下标访问不存在的元素将导致在map容器中添加一个新元素,它的键即为该下标值,值为值类型的默认初始化值。
第11章泛型算法
1.泛型算法中,所谓“泛型“指的是两个方面:
这些算法可作用与各种不同的容器类型,而这些容器又可以容纳多种不同的元素。
泛型算法必须包含<
algorithm>
头文件,算术算法还必须包含<
numerc>
头文件。
插入器(inserter)包含在<
interator>
头文件中。
2.泛型算法的形参模式:
alg(beg,end,beg2,end2,otherparms)、
beg,end标记第一个范围,这两个参数泛型算法都有;
beg2,end2标记第二个范围,可能没有这个两个参数;
parms表示其他算法需要的值或谓词。
3.算法不直接修改容器的大小,如果需要添加和删除元素,则必须使用容器操作。
4.泛型算法的分类
∙只读算法:
只读输入范围内的元素,而不会写这些元素。
find(),accumulate(),find_first_of()是只读算法;
∙写容器元素算法:
将数据写入第一或第二输入范围。
fill(),fill_n(),copy(),replace()都是这类算法。
直接将元素写入目标很危险,因为泛型算法不会调用容器提供的操作(如insert)(相当于用下标读vector的内容,再修改),所以很类算法一般要和插入迭代器配合使用。
有三种插入迭代器:
back_inserter,创建使用push_back实现插入的迭代器;
front_inserter,使用push_front实现插入(不能用在vector上);
inserter,使用insert实现插入。
vector<
int>
ivec;
replace_copy(ilist.begin(),ilist.end(),inserter(ivec,ivec.begin()),0,42);
∙对容器元素重新排序的算法:
sort(),unique()
5.五种迭代器
∙输入迭代器:
读,不能写;
只支持自增运算。
istream_iterator是输入迭代器
∙输出迭代器:
写,不能读;
ostream_iterator是输出迭代器
∙前向迭代器:
读和写,只支持自增运算。
replace算法需要前向迭代器
∙双向迭代器:
读和写,支持自增和自减运算,map、set、list提供双向迭代器。
Reverse算法需要双向迭代器
∙随机访问迭代器:
读和写,支持完整的迭代器算术运算,string、vector和deque提供双向迭代器。
6.list容器特有的算法
list上提供双向迭代器,故很多需要随机访问迭代器的算法不能在其上运行,故标准库为list容器定义了更精细的操作集合,如merge(),remove(),sort(),reverse(),splice(),unique()
第12章类
1.在类内部定义的成员函数,将自动作为inline处理。
也可以显式的将成员函数声明为inline。
inline成员函数的定义必须在调用该函数的每个源文件中是可见的,故inline函数的定义通常放在定义该类的头文件中。
2.类声明:
为了在类定义之前使用它,我们可以先声明它,此时该类称为不完全类型。
不完全类型只能以有限访问时用。
不能定义该类型的对象。
不完全类型只能用于定义指向该类型的指针及引用,或者用与声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
类的声明一般用来编写相互依赖的类。
3.可以把数据成员声明为mutable(不能同时为const修饰),mutable数据成员可以在const成员函数中修改。
4.当成员函数的返回类型在类中定义时,而且是在类外定义的,则定义时需要使用完全限定名。
5.构造函数不能声明为const。
必须对任何const或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式。
使用构造函数初始化列表初始化,数据成员被初始化的次序就是类定义成员的次序,而不是在构造函数初始化列表中的次序。
6.使用默认实参的构造函数能减少代码重复。
7.只有当一个类没有定义构造函数时,编译器才会自动生成一个默认构造函数。
8.隐式类类型转换:
可以用单个实参调用的构造函数,定义了从形参类型到该类类型的一个隐式转换。
可以将构造函数声明为explicit,来防止隐式转换。
explicit只能用于类内部声明上(不能用于定义上)。
9.注意友元声明的顺序:
声明类A,把类A声明为B的友元(在类B的定义中),定义类A(需要用到B的定义)。
10.static类成员
∙static成员函数不是任何对象的组成部分,故没有this形参,可以直接使用类的static成员,但不能直接使用非static成员,不能被声明为const和虚函数。
可以使用类作用域操作符从类中直接调用static成员,也可以通过对象调用。
∙static数据成员必须在类定义体的外面定义(正好一次),不能通过类构造函数初始化,而是应该在外面定义时初始化。
static关键值只能用于类定义体内部的声明中,定义时不能标示为static。
conststatic数据成员可以在类的定义体中初始化,但仍必须在类的定义体外部定义(不必再指定初始化值)。
∙static成员不同与非static成员的使用:
static数据成员的类型可以是该成员所属的类类型(ClassBar{staticBarmem;
}),非static成员被限定声明为其自身类对象的指针或引用(因为这时类还没定义完,相当于类的向前声明);
同样,static数据成员可以作默认实参,非static不能(因为它不能独立于所属对象)。
第13章复制控制
1.复制控制函数包括复制构造函数、赋值操作符、析构函数
∙复制构造函数:
只有单个参数,而且该形参是对本类类型对象的引用(常用const修饰),如T(constT&
)。
如果没有定义复制构造函数(即使定义了其他构造函数),编译器会为我们合成一个——执行逐个成员初始化,将新对象初始化为原对象的副本。
所以为了防止复制,类必须显式声明其复制构造函数为private。
不允许复制的类对象只能作为引用或指针传递给函数或从函数返回,也不能用作容器的元素。
∙赋值操作符:
就是重载’=‘操作符,如T&
operator=(constT&
)(注意返回值为类的引用,因为=返回左操作符)。
合成赋值操作符,将右操作数对象的每个成员赋值给左操作数对象的对应成员,还能对数组赋值。
复制和赋值常一起使用。
∙析构函数:
构造函数的互补,释放对象获得的资源。
合成构造函数不会自动删除指针对象所指向的对象。
如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则,常称为三法则(ruleofthree)。
析构函数与复制构造函数或赋值操作符之间的一个重要区别是:
即使我们编写了自己的析构函数,合成析构函数仍然运行。
2.管理指针成员:
包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不会复制指针指向的对象。
∙默认(合成)复制/赋值管理指针,指针共享同一对象,可能出现悬垂指针。
∙用智能指针管理指针复制/赋值:
新定义一个类管理指针,并增加一个计数功能,当复制/赋值时计数加一,对象销毁时(析构函数)减一,当计数为0时,释放指针。
∙定义值型类:
给指针成员提供值语义,创建一个新对象。
3.复制/赋值操作要考虑自己复制/赋值自己。
第14章重载操作符与转换
1.重载操作符不能创建任何新的操作符,不能改变操作符的优先级、结合性或操作数数目。
2.重载操作符的定义
i.形式是保留字operator后接许定义的操作符符号,如
itemoperator+(constitem&
constitem&
ii.重载操作符必须具有至少一个类类型或枚举类型的操作符。
这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含义。
iii.重载操作符可以定义为类的成员函数或非成员函数
i.作为类成员的重载函数,其形参看起来比操作数数目少1。
作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数(故输入输出操作符不能重载为类成员)
ii.操作符定义为非成员函数时,通常必须将他们设置为所操作类的友元,以访问类的私有变量。
3.重载操作符的设计
i.不要重载具有内置含义的操作(逗号、取地址、逻辑与、逻辑或等操作通常不重载)
ii.重载操作符是为了使用方便,当一个重载操作符的含义不明显,或操作很少时,没必要重载操作符,定义普通函数就行了。
iii.选择成员或非成员的实现:
赋值(=)、下标([])、调用(())、成员访问(->
)、转换操作符等操作必须定义为成员;
输入和输出操作符必须定义为非成员;
对等的操作符,如算法操作符、相等操作符、关系操作符和位操作符等对称的操作符,最好定义为非成员;
改变对象状态或与给定类型紧密联系的一些操作符,如自增、自减和解引用,通常定义