未完待续!C++ Primer中文版第五版阅读知识点提取!

前三章基本语法被略去:

1.decltype(s.size()) punct_cnt=0; 声明类型操作//p109

2.//养成使用迭代器和!=的好习惯,就不用太在意用的到底是哪种容器类型

3.string和vector可以使用下标操作,其他大多数容器都不能使用下标操作
严格的说string不算容器,但是它拥有很多和容器一样的操作函数。string也有迭代器

4.但凡使用了迭代器的循环体,都不要向迭代器所属的容器中添加元素,迭代器会失效的!!

5.string和vector都定义了difference_type类型,是带符号类型。

6.不存在引用的数组

7.数组使用begin和end函数,这两个函数不是成员函数,和容器的不同,数组不是类类型

8.ptrdiff_t与size_t的都一样是定义在cstddef头文件中的机器相关的类型,ptrdiff_t是一种带符号类型

9.数组的下标可以为负,string和vector的下标不可以

10.string类型的变量执行完c_str()函数后,如果改变string类型变量,那么c_str()函数返回的数组也会改变。

11.C++11新标准中新增了范围for语句

12.要使用范围for语句处理多维数组,除了最内存的循环外,其他所有循环的控制变量都应该是引用类型

13.getline 是string头文件中定义的一个函数,getline(cin,s);//s为string类型

第四章:表达式
14.输出表达式未定义问题,假设n=10
cout << n << " " << ++n << endl; //表达式的行为不可预知,编译器无论产生什么结果都是错误的。
还有几个函数返回值相加,如果其中几个函数影响同一推向,则他是一条错误的表达式,产生未定义行为

15.(-m)/n和m/(-n)都等于-(m/n), m%(-n)等于m%n ,(-m)%n等于-(m%n)

16.逻辑与和逻辑或运算符()

17.建议:除非必须,否则不用递增递减运算符的后置版本,后置版本将对象原始值的副本作为右值返回。
就是说后置版本需要储存原始值,如果非必要情况,相对于复杂的迭代器类型,这种额外的工作就消耗巨大了。
养成使用++i的好习惯!

18.后置递增运算符的优先级高于解引用运算符,因此pbeg++等价于(pbeg++)

19.条件运算嵌套层数最好别超过两到三层,会使得代码的可读性急剧下降。

20.符号位如何处理没有明确的规定,不同编译器处理方式不同,所以强烈建议仅将位运算符用于处理无符号类型

21.cast-name(expression) 强制类型转换 cast-name是static_cast,dynamic_cast,const_cast和reinterpret_cast中的一种

第五章:语句

22.try语句块和异常处理
throw表达式:异常检测部分使用throw表达式来表示遇到无法处理的问题。称throw引发了异常。
try语句块:异常处理部分使用try语句块处理异常。try语句块以关键字try开始,并以一个或多个catch子句结束。try语句块中代码抛出的异常通常会被某个catch子句处理。因为catch子句"处理"异常,所以他们也被称作异常处理代码。
一套异常类,用于在throw表达式和相关的catch子句之间传递异常的具体信息。
ps:编写异常安全的代码非常困难,异常中断了程序的正常流程。

23.标准异常在4个头文件中:exception,stdexcept,new(定义了bad_alloc异常类型),type_info(定义了bad_cast异常类型)

24.异常类型定义了一个名为what的成员函数,没有参数,返回值是一个const char*类型,为了提供关于异常的一些文本信息。

25.只有exception,bad_alloc,bad_cast对象能以默认初始化方式初始化,其他对象不行,要用string对象或者C风格字符串初始化。

第六章:函数
函数基础
参数传递
返回类型和return语句
函数重载
特殊用途语言特性
函数匹配
函数指针

26.如果局部静态变量没有显式初始值,它将执行值初始化内置类型的局部静态变量初始化为0

27.函数声明也称函数原型

28.当使用argv中的实参时,记得可选的实参从argv[1]开始,argv[0]保存程序的名字,而非用户输入。

29.为了编写能处理不同数量实参的函数,C++新标准提供了两种主要的方法:1.如果所有的实参类型相同,可以传递一个名为initializer_list的标准库类型;2.如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。

30.C++还有一种特殊的形参类型(即省略符),可以用它传递可变数量的实参。这种功能一般只用于与C函数交互的接口程序。

31.省略符形参是为了便于C++程序访问某些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能。

32.列表初始化返回值:C++11新标准规定,函数可以返回花括号包围的值的列表。给返回值为vector等类类型初始化。

33.数组不能被拷贝,所以函数不能返回数组,但是函数可以返回数组的指针或引用。
eg:typedef int arr[10];
using arr=int[10];
arr* func(int i);
eg2:
int arr[10];
int (*p)[10]=&arr;

34.使用尾置返回类型
auto func(int i)->int (*)[10];

35.重载和const形参:顶层cosnt不影响传入函数的对象。一个拥有顶层const的形参无法和另一个没有顶层const的形参区别开来。

36.const_cast和重载:如果函数返回值是一个const引用类型,但是想要返回一个不同的类型,那么久可以在返回时候用到const_cast去除const性。

37.特殊用途的语言特性三个:默认实参、内联函数和constexpr函数
默认实参声明:ps:在给定的作用域中一个形参只能被赋予一次默认实参。换句话说,函数的后续声明只能为之前那些没有默认值的形参添加实参,而且该形参右侧的所有形参必须都有默认值。
string screen(sz,sz,char=’ ‘);
string screen(sz,sz,char=’*’);//err:重复声明
string screen(sz=24,sz=80,char); //正确:添加默认实参

38.内联函数和constexpr函数
内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求。
constexpr函数是指能用于常量表达式的函数。执行该初始化任务时,编译器把对constexpr函数的调用替换成其结果值。为了能在编译过程中随时展开,constexpr函数被隐式地指定为内联函数。ps:constexpr函数不一定返回常量表达式。
ps:通常把内联函数和constexpr函数放在头文件内(它们的多个定义必须完全一致)

39.调试帮助方法用两种预处理功能:assert(预处理宏)和NDEBUG(预处理变量)
调试时的几个变量:
func //输出当前调试的函数名字
FILE
LINE
TIME
DATE

40.所有算数类型转换的级别都一样:
eg: void manip(long);
void manip(float);
manip(3.14);//err:二义性调用 double可以转换long或者float

41.函数匹配和const实参
ps:能不类型转换就不类型转换

42.使用函数指针
当我们把函数名作为一个值使用时,该函数自动地转换成指针。(写不写&都一样)

43.函数指针形参
形参是函数类型,它会自动地转换成指向函数的指针。
类型别名和decltype可以简化使用函数指针代码。typedef decltype(AAA) *Func;

44.返回指向函数的指针(可以直接声明或用简单类型别名声明方法)
简单声明using p=int(*)(int *,int); //p是指针类型
ps:和函数类型的形参不一样,返回类型不会自动地转换成指针,必须显式地将返回类型指定为指针。

45.将auto和decltype用于函数指针类型
decltype作用于某个函数时,它返回函数类型而非指针类型。

第七章 类

46.类的基本思想是数据抽象和封装。数据抽象是一种依赖于接口和实现分离的编程技术。

47.引入const成员函数,类函数参数后面的const的作用是修改隐式this指针的类型。

48.定义类相关的非成员函数
一般来说,如果非成员函数是类接口的组成部分,则这些函数的声明应该与类在同一个头文件内。

49.IO类属于不能被拷贝的类型,因此我们只能通过引用来传递它们。

50.构造函数不能被声明为const的

51.只有当类没有声明任何构造函数时,编译器才会自动地生成默认构造函数。

52.如果类包含有内置类型或者复合类型的成员,则只有当这些成员全都被赋予了类内的初始值时,这个类才适合于使用合成的默认构造函数。

53.C++11新标准,如果我们需要默认的行为,可以通过在参数列表后面写上=default来要求编译器生成构造函数。

54.构造函数不应该轻易覆盖掉类内的初始值,除非新赋的值与原值不同。如果你不能使用类内初始值,则所有构造函数都应该显式地初始化每个内置类型的成员。

55.使用class和struct关键字唯一一点区别是,struct和class的默认访问权限不太一样。
struct是默认public的 class 是默认private的

56.友元声明只能出现在类定义的内部,但是在类内出现的位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束。建议:最好在类定义开始或者结束前的位置集中声明友元。
友元声明仅仅指定了访问的权限,非通常意义上的函数声明。如果想类用户可以调用友元函数,必须对函数进行一次声明才可以。

57.封装的两个重要有点:1.确保用户代码不会无意间破坏封装对象的状态。2.被封装的类的具体实现可以随时改变,而无须调整用户级别的代码。 (封装还可以将bug限制在有限的范围内)

58.类还可以用typedef来自定义某种类型在类中的别名或者使用using类型别名来等价。
eg:typedef std::string::size_type pos; 都是先定义 后使用*
eg2:using pos=std::string::size_type;

59.希望修改类的某个数据成员,即使是在一个const成员函数内,可通过在变量的声明中加入mutable关键字做到这一点。

60.C++11新标准可以把类数据成员默认值声明成一个类内初始值。
必须使用=号的初始化形式或者花括号直接初始化形式

61.一个const成员函数如果以引用的形式返回*this,那么它的返回类型将是常量引用。

62.通过区分成员函数是否是const的,我们可以对其进行重载**

63.建议:对于公共代码使用私有功能函数
1)避免代码复用
2)随着规模发展,公共代码将变得复杂
3)开发过程中可在功能函数中添加调试信息,添加与删除将变得简单。
4)额外的函数调用不会增加任何开销。类内部公共代码会隐式地被声明为内联函数。

64.即使两个类的成员列表完全一致,它们也是不同的类型。

65.类对象的前向声明,在它声明之后定义之前是一个不完全类型,不清楚它到底包含哪些成员。

66.友元函数能定义在类的内部,这样的函数是隐式内联的。还可以把其他的类定义为友元,也可以把其他类的成员函数定义为友元。

67.函数重载的不同函数需要每一个分别进行函数声明成友元才可以全部生效。不然只有声明成友元的函数才有作用。

68.编译器处理完类中的全部声明后才会处理成员函数的定义。***********

69.类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义后。

70.不建议使用其他成员的名字作为某个成员函数的参数,会隐藏同名的成员。
尽管类的成员被隐藏了,但我们仍然可以通过加上类的名字或显式地使用this指针来强制访问成员。

71.如果成员是const、引用,或者属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初值。 ps:使用构造函数初始值***

72.最好令构造函数初始值的顺序与成员声明的顺序保持一致。而且如果可能的话,尽量避免使用某些成员初始化其他成员。

73.如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。

74.C++11新标准扩展了构造啊还念书初始值的功能,使得我们可以定义所谓的委托构造函数**

75.在实际中,如果定义了其他构造函数,那么最好也提供一个默认构造函数。

76.能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则。
只允许一步类类型转换,超过一步不能转换了。类类型转换不是总有效
可以通过将构造函数声明为explicit抑制构造函数定义的隐式转换。(只对一个实参的构造函数有效)
explicit关键字只允许出现在类内的构造函数声明处。
当我们用explicit关键字声明构造函数时,它将只能以直接初始化的形式使用。而且,编译器将不会在自动转换过程中使 用该构造函数。

77.聚合类:所有成员public的,没有定义任何构造函数,没有类内初始值吗没有基类和virtual函数

78.类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据。

79.和类的所有成员一样,当我们指向类外部的静态成员时,必须指明成员所属的类名。static关键字则只出现在类内部的声明语句中。

80.要想确保对象只定义一次,最好的办法就是把静态数据成员的定义与其他非内联函数的定义放在同一文件中。

81.即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类的外部定义一下该成员。

第二部分
C++标准库:
第八章:IO库
第九章:顺序容器
第十章:泛型算法
第十一章:关联容器
第十二章:动态内存

第八章:IO库暂时粗略浏览
82.IO对象无拷贝或赋值
IO库条件状态表
管理输出缓冲
unitbuf操纵符cout<<unitbuf; 立即刷新缓冲区 和cout<<nounitbuf; 回到正常缓冲模式
警告:如果程序崩溃,输出缓冲区不会被刷新
83.交互式系统通常关联输入和输出流。 cin>>ival;

84.当一个fstream对象被销毁时,close会自动被调用。

85.文件模式有: in out app ate trunc binary

第九章:顺序容器(通常使用vector是最好的选择)
vector、deque、list、forward_list、array、string
86.list和forward_list(没有size操作)两个容器任何位置添加和删除操作都很快速。作为代价,这两个容器不支持元素的随机访问:只能遍历整个容器才能访问一个元素,而且这两个容器额外内存开销也很大。

87.deque(更为复杂的数据结构)两端添加和删除操作速度和list和forward_list相当。可以随机访问元素。

88.容器操作表(p295)

89.以c开头的cbegin是c++新标准引入的,用以支持auto与begin和end函数结合使用。

90.只有顺序容器的构造函数才接受大小参数,关联容器并不支持。

91.标准库array具有固定大小array<int,42> a;

92.不能对内置数组类型进行拷贝或对象复制才做,但array并无此限制。***

93.assign(替换)操作不适用于关联容器和array

94.由于其旧元素被替换,因此传递给assign的迭代器不能指向调用assign的容器。

95.赋值相关运算会导致指向左边容器内部的迭代器、引用和指针失效。而swap操作将容器内容交换不会导致指向容器的迭代器、引用和指针失效(容器类型为array和string的情况除外)。

96.除array外,swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。

97.在新标准库中,容器既提供成员函数版本的swap,也提供非成员版本的swap。
统一使用非成员版本的swap是个好习惯,在泛型编程中是非常重要的。

98.forward_list支持max_size和empty,但是不支持size

99.只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器。

100.除array和forward_list(但是支持push_front)之外,每个顺序容器(包括string类型)都支持push_back

101.关键概念:容器元素是拷贝(p306)

102.将元素插入到vector、deque和string中的任何位置都是合法的。然而,这样做可能很耗时。

103.新标准引入了三个新成员 emplace_front、emplace和emplace_back,这些操作构造而不是拷贝元素。

104.emplace函数在容器中直接构造元素。传递给emplace函数的参数必须与元素类型的构造函数相匹配。

105.at和下标操作只适用于string、vector、deque、和array。

106.提供快速随机访问的容器(string、vector、deque、和array)也都提供下标运算符。
106.在容器中分访问元素的成员函数(eg:front,back,下标和at)返回的都是引用。

107.forward_list有特殊版本的erase
forwar_list不支持pop_back;vector和string不支持pop_front.
back不适用与forward_list

108.删除deque中除首尾位置之外的任何元素都会使所有迭代器、引用和指针失效。指向vector或string中删除点之后位置的迭代器、引用和指针都会失效。 删除元素的成员函数并不检查其参数。删除前必须确保它存在。

109.forward_list中的变种insert,emplace和erase是insert_after、emplace_after、erase_after的操作。
它还可以在首元素之前加元素,before_begin();

110.改变容器大小操作resize();[不适用与array]

111.不要保存end返回的迭代器,end 返回的迭代器总是会失效的。

112.shrink_to_fit只适用于vector、string、deque
capacity和reserve只适用于vector和string
调用reserve之后,capacity将会大于或者等于传递给reserve的参数。

113.vector 的实现采用的策略似乎是在每次需要分配新内存空间时将当前容量翻倍。
调用shrink_to_fit 只是一个请求,标准库并不保证退还内存。

114.string类型:substr、assign(替换)、insert、erase、append(尾插)、replace(调用erase和insert的一种简写)、
find(返回下标或npos)和rfind、find_first_of、find_first_not_of、find_last_of、find_last_not_of、
compare、to_string()、stod()[将字符串转化为浮点数],stdoi,stdol…

115.容器适配器:顺序容器适配器stack(不接受array和forward_list)、queue、priority_queue

116.stack可以使用除array和forward_list之外的任何容器类型来构造,栈默认基于deque实现,也可以在list或vector上实现,queue适配器可构造于list或deque(默认)之上,但不能基于vector构造。priority_queue可以构造于vector(默认)或deque之上,但不能基于list构造。

第十章:泛型算法
概述
初始泛型算法
定制操作
再探迭代器
泛型算法结构
特定容器算法

117.关键概念:算法永远不会执行容器的操作(p337)
算法可以改变容器中保存的元素的值,也可能在容器内移动元素,但永远不会直接添加或删除元素。

118.标准库提供了超过100个算法,附录A按操作方式列出了所有算法

119.只读算法:eg:find、count、accumulate(头文件numeric中)、equal
accumulate的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型。
string sum=accumulate(v.cbegin(),v.cend(),string("")); // 累加容器中所有string元素

120.equal算法中那些只接受一个单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长

121.写容器元素的算法:fill(v.begin(),v.end(),0) 最多写入与给定序列一样多的元素

122.关键概念:迭代器参数,三个迭代器的算法会有危险,如果访问了第二个序列中末尾之后不存在的元素,就产生一个严重错误。

123.算法不检查写操作
fill_n(v.begin(),v.size(),0); //如果v容器是空的,那就是灾难性修改
向目的位置迭代器写入数据的算法假定目的位置足够大,能容纳要写入的元素。

124.介绍 back_inserter(头文件iterator)
一种保证算法有足够元素空间来容纳输出数据的方法是使用插入迭代器
fill_n(back_inserter(v),10,0);

125.copy拷贝算法接受三个参数、(replace算法接受四个参数,如果想保留原序列不变,可以调用replace_copy)
replace_copy(v.cbegin(),v.cend(),back_inserter(vec),0,42);

126.重排容器元素的算法:sort算法,利用元素类型的<运算符来实现排序的。
unique的标准库算法

127.标准库算法对迭代器而不是容器进行操作。因此,算法不能直接添加或删除元素。

128.调用erase是安全的,即使为空容器,删除一个空范围没有不良后果。

129.定制操作:可以重载sort(可接受第三个参数谓词)的默认行为。向算法传递函数,接受一个谓词参数。

130.谓词:一元谓词和二元谓词(根据参数不同命名)

131.排序算法:为了保持相同长度的单词按字典序排列,可用stable_sort算法。

132.lambda表达式:有的算法接受一元谓词才能调用,但是一元谓词有事满足不了需求,所以产生了lambda表达式。

133.如果lambda的函数体包含任何单一return语句之外的内容,且未指定返回类型,则返回void。
lambda函数不能有默认参数,一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该 变量。

134.捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。

135.当以引用方式捕获一个变量时,必须保证在lambda执行时变量是存在的。
建议:尽量保持lambda的变量捕获简单化。
可以混用隐式捕获和显示捕获[&,c] [=,&os]

136.默认情况下,如果一个lambda体包含return之外的任何语句,则编译器假定此lambda返回void。当然也可以自己指定返回类型。
对于一个值被拷贝的变量,默认lambda不会改变其值,但是可以加上mutable来改变被拷贝的变量。[可变lambda]

137.参数绑定:
标准库bind函数(functional头文件中)
用bind可以重排参数顺序
如果我们希望传递给bind一个对象而又不拷贝它,就必须使用标准库ref(functional头文件)函数
for_each(words.begin(),words.end(),bind(print,ref(os),_1,’ '));
标准库好友一个cref函数,生成一个保存const引用的类。

138.使用placeholders名字
using std::placeholders::_1
using namespace std::placeholders;

139.向后兼容:参数绑定
旧版本的bind1st和bind2nd已被弃用,新版本不再支持了。

140.再探迭代器:
插入迭代器:
back_inserter:只有容器支持push_back的情况下才能用
front_inserter:只有容器支持push_front的情况下才能用
inserter:此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。
流迭代器:
istream_iterator:允许使用懒惰求值
ostream_iterator:想此对象赋值时,可以忽略解引用和递增运算,但不建议。
反向迭代器:(除了forward_list之外的标准库容器都有反向迭代器)rbegin、rend、crbegin、crend
不可能从一个forward_list或一个流迭代器创建反向迭代器。
如果使用反向迭代器,会反向处理string使得和需要的字符串相反,要调用reverse_iterator的base成员函数来完成转换。
移动迭代器:

141.泛型算法结构:
5个迭代器类别:
输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。
警示:对于向一个算法传递错误类别的迭代器的问题,很多编译器不会给出任何警告或提示。
输入迭代器:只能用于单遍扫描算法,可用于finde和accumulate算法
输出迭代器:只能用于单遍扫描算法,copy第三个参数和ostream_iterator类型是输出迭代器。
前向迭代器:可多遍扫描,eg:replace要求前向迭代器,forward_list上的迭代器是前向迭代器。
双向迭代器:算法reverse要求双向迭代器,除了forward_list之外,其他标准库都提供符合双向迭代器要求的迭代器。
随机访问迭代器:提供在常量时间内访问序列中任意元素的能力。
算法sort要求随机访问迭代器。array、deque、string、和vector的迭代器都是随机访问迭代器,用于访问内置数组元素的指针也是。

142.算法形参模式:

向输出迭代器写入数据的算法都假定目标空间足够容纳写入的数据。
_if版本的算法都接受谓词参数。

143.区分拷贝元素的版本和不拷贝的版本
算法后面附加一个_copy
reverse_copy
还有同时提供_copy和_if的版本
remove_cpoy_if(v.begin(),v.end(),back_inserter(v2),[](int i){return i%2;});

144.特定容器算法:
链表类型定义的其他算法的通用版本可以用于链表,但代价太高。
对于list和forward_list,应该优先使用成员函数版本的算法而不是通用算法。
他们定义了独有的sort、merge、remove、reverse和unique。
splice成员:链表类型还定义了splice算法(无通用版本,是链表数据结构所特有的)

145.链表特有的操作会改变容器:

第十一章:
关联容器
使用关联容器
关联容器概述
关联容器操作
无序容器

关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。两个主要的关联容器类型是map和set。标准库提供8个关联容器,允许重复关键字的容器的名字都包含multi,不保持关键字按顺序存储的容器的名字都以单词unordered开头。
set则是一个要求不重复关键字,有序存储的集合。

146.有序容器的关键字类型
所提供的操作必须在关键字类型上定义一个严格弱序,可将其看作“小于等于”
147.pair类型(头文件 utility):保存两个数据成员,是一个用来生成特定类型的模板。
与其他标准库类型不同,pair的数据成员是public的,两个成员分别命名first和second。

148.make_pair(v1,v2) 返回一个用v1和v2初始化的pair。pair的类型从v1和v2类型推断出来。

149.创建pair 对象的函数
pair<string,int> process(vector &v)
{
if(!v.empty()) //return make_pair(v.back(),v.back().size());
return {v.back(),v.back().size()}; //这个是新标准支持//return pair<string,int>(v.back(),v.back().size());
else
return pair<string,int>(); //隐式构造返回值
}

150.关联容器额外的类型别名
key_type mapped_type value_type
set类型中,key_type和value_type是一样的
map中,每一个元素是一个pair对象。
只有map类型才定义了mapped_type。

151.必须记住,一个map的value_type是一个pair,我们可以改变pair的值,但不能改变关键字成员的值。

152.虽然set类型同时定义了iterator和const_iterator类型,但是两种类型都只允许只读访问set中的元素。

153.关联容器和算法:
通常不对关联容器使用泛型算法,关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,其第一个成员是const的。
关联容器可用于只读取元素的算法,但是,很多这类算法都要搜索序列。由于关联容器中的元素不能通过他们的关键字进行(快速)查找,因此对其使用泛型搜索算法几乎总是个坏主意。

154.向map中添加元素有4种方法
m.insert({word,1});
m.insert(make_pair(word,1));
m.insert(pair<string,size_t>(word,1));
m.insert(map<string,size_t>::value_type(word,1));

155.检测map中insert的返回值:auto ret=word_count.insert({word,1});
返回值ret类型为:pair<map<string,size_t>::iterator,bool>

156.向multiset或multimap添加元素:
对允许重复关键字的容器,接受单个元素的insert操作返回一个指向新元素的迭代器。这里无须返回一个bool值,因为insert总是向这类容器中加入一个新元素。

157.从关联容器删除元素(3种方式)

158.map的下标操作:
map和unordered_map容器提供了下标运算符和一个对应的at函数。set类型不支持下标,因为set中没有与关键字相关联的“值”。multimap或一个unordered_multimap不能进行下标操作,可能出现多个值与一个关键字相关联。

159.map使用下标操作,与数组或vector不同,使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中。如果不想添加,可以用at函数,关键字不存在会抛出一个out_of_range异常。

160.下标和at操作只适用于非const的map和unordered_map中。

161.对map使用find代替下标操作

162.lower_bound返回的迭代器可能指向一个具有给定关键字的元素,但也可能不指向。如果关键字不再容器中,lower_bound会返回关键字的第一个安全插入点–不影响容器中元素顺序的插入位置。
可以用equal_range函数解决multimap中查找问题。

163.如果lower_bound和upper_bound返回相同的迭代器,则给定关键字不再容器中。

164.无序容器:新标准定义了4个无序关联容器。
这些容器不是使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的==运算符。
如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器。
除了哈希管理操作之外,无序容器还提供了与有序容器相同的操作。因此,通常可以用一个无序容器替换对应的有序容器。

165.管理桶:
无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。

166.无序容器管理操作:(p395)
桶接口(4个) 桶迭代(4个) 哈希策略(4个)

167.无论在有序容器中还是在无序容器中,具有相同关键字的元素都是相邻存储的。

第十二章:
动态内存
1.动态内存与智能指针
2.动态数组
3.使用标准库:文本查询程序

168.动态内存与智能指针
为了更容易(同时也更安全)地使用动态内存,新的标准库提供了两种智能指针类型来管理动态对象。
shared_ptr允许多个指针指向同一个对象;unique_ptr则“独占”所指向的对象。
标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。

169.如果你将shared_ptr存放于一个容器中,而后不再需要全部元素,而只使其中一部分,要记得用erase删除不再需要的那些元素。

170.使用了动态生存期的资源的类:
程序使用动态内存出于以下三种原因之一:
1.程序不知道自己需要使用多少对象(eg:容器类)
2.程序不知道所需对象的准确类型
3.程序需要在多个对象间共享数据

171.使用动态内存的一个常见原因是允许多个对象共享相同的状态。

172.出于与变量初始化相同的原因,对动态分配的对象进行初始化通常是个好主意。

173.动态分配的const对象
const int *pci=new const int(1024);

174.内存耗尽:
会抛出一个类型为bad_alloc的异常,可以阻止异常抛出,使其返回空指针
int *p=new (nothrow) int; //这种形式叫定位new
bad_alloc和nothrow都定义在头文件new中

175.释放一个空指针总是没有错误的。
指向const对象的指针可以delete

176.由内置指针(而不是智能指针)管理的动态内存在被显式释放前一直都会存在。

177.小心:动态内存的管理非常容易出错
1.忘记delete内存
2.使用已经释放掉的对象
3.同一块内存释放两次
坚持只使用智能指针,就可以避免所有这些问题。对于一块内存,只有在没有任何智能指针指向它的情况下,智能指针才会自动释放它。

178.delete之后重置指针值为nullptr,只提供有限保护,eg:两个指针指向同一块动态内存,实际系统中,查找指向相同内存的多有指针是异常困难的。

179.shared_ptr和new结合使用
shared_ptr p2(new int(42));
不能用new返回的指针来初始化智能指针,接受指针参数的智能指针构造函数是explicit的。

180.不要混合使用普通指针和智能指针
智能指针内部有一个引用计数,减到0时会自动销毁动态内存
拷贝会递增它的引用计数

181.使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时会被销毁。

182.也不要使用get初始化另一个智能指针或为智能指针赋值
get用来将指针的访问权限传递给代码,你只有在确定代码不会delete指针的情况下,才能使用get。特别是,永远不要用get初始化另一个智能指针或者为另一个智能指针赋值。

183.智能指针和异常:
如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不再需要时将其释放。

184.智能指针和哑类
有些类不具有良好定义的析构函数,可用智能指针来保证其内存释放。

185.注意:智能指针陷阱
正确使用智能指针:
1.不使用相同的内置指针值初始化(或reset)多个智能指针。
2.不delete get()返回的指针。
3.不使用get()初始化或reset另一个智能指针。
4.如果你使用get()返回的指针,记住当最后一个对应的智能指针销毁后,你的指针就变成无效了。
5.如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除器。

186.unique_ptr:
由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作。

187.向后兼容:auto_ptr
它具有unique_ptr的部分特性,但不是全部。特别是,不能再容器中保存auto_ptr,也不能从函数中返回auto_ptr。
虽然auto_ptr仍是标准库的一部分,但编写程序时应该使用unique_ptr。

188.向unique_ptr传递删除器
重载一个unique_ptr中的删除器会影响到unique_ptr类型以及如何构造(或reset)该类型的对象。

189.weak_ptr是一种不控制所指向对象生存期的智能指针。

190.核查指针类
eg:可以阻止用户访问一个不再存在的vector的企图。

191.大多数引用应该使用标准库容器而不是动态分配的数组。使用容器更为简单、更不容易出现内存管理错误并且可能有更好的性能。

192.分配一个数组会得到一个元素类型的指针

193.要记住我们所说的动态数组并不是数组类型,这是很重要的。

194.初始化动态分配对象的数组
在新标准中,我们还可以提供一个元素初始化器的花括号列表来初始化。

195.new可能会抛出一个类型为bad_array_new_length的异常。类似bad_alloc,此类型定义定义在头文件new中。

196.当我们用new分配一个大小为0的数组时,new返回一个合法的非空指针。此指针保证与new返回的其他任何指针都不相同。此指针可以像尾后迭代器一样使用,可以进行比较操作。

197.delete[]释放动态数组,元素按照逆序销毁。*******

198.与unique_ptr不同,shared_ptr不直接支持管理动态数组。如果希望使用shared_ptr管理一个动态数组,必须提供自己定义的删除器。

199.shared_ptr不直接支持动态数组管理这一特性会影响我们如何访问数组中的元素:
*(sp.get()+i)=i; //使用get获取一个内置指针。

200.allocator类:是一个模板(头文件memory中)
我们希望将内存分配和对象构造分离。我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作(同时付出一定的开销)。

201.分配未构造的内存:
为了使用allocate返回的内存,我们必须用construct构造对象。使用未构造的内存,其行为是未定义的。

202.我们只能对真正构造了得元素进行destroy操作。

203.拷贝和填充未初始化内存的算法:都定义在头文件memory中
uninitialized_copy与uninitialized_copy_n 和uninitilized_fill与uninitilized_fill_n

第三部分:
类设计者的工具
第十三章:拷贝控制
第十四章:操作重载与类型转换
第十五章:面向对象程序设计
第十六章:模板与泛型编程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值