C++ primer
这是一份自己对C++ primer的精读笔记,现在对C++基本是个小白状态,故而这份笔记也仅分享一个初学者的阅读状态,计划在半年左右将该书读完,尽可能做一些对原文的理解注释与整理归纳,期以之提高自己的C++基础语法水平。
——2023.4.25
推荐序1
“新版标准中引入了无序容器,以弥补原版标准中对
h
a
s
h
hash
hash容器的缺漏”
在原本的传统C++中,我们仅有有序容器
s
t
d
:
:
m
a
p
std::map
std::map与
s
t
d
:
:
s
e
t
std::set
std::set,其底层通过“红黑树”实现,搜索与插入的平均时间复杂度为
O
(
l
o
g
N
)
O(logN)
O(logN)其中
N
N
N为问题规模,而无序容器中的元素不进行排序,内部通过“
h
a
s
h
hash
hash表”实现,插入和删除元素的时间复杂度为
O
(
1
)
O(1)
O(1),在不关心内部元素顺序时,能显著提高性能。
引入的无序容器
1.
s
t
d
:
:
u
n
o
r
d
e
r
e
d
_
m
a
p
std::unordered\_map
std::unordered_map与
s
t
d
:
:
u
n
o
r
d
e
r
e
d
_
m
u
l
t
i
m
a
p
std::unordered\_multimap
std::unordered_multimap
2.
s
t
d
:
:
u
n
o
r
d
e
r
e
d
_
s
e
t
std::unordered\_set
std::unordered_set与
s
t
d
:
:
u
n
o
r
d
e
r
e
d
_
m
u
l
t
i
s
e
t
std::unordered\_multiset
std::unordered_multiset
“红黑树”
“新版标准支持移动构造函数和移动赋值运算符,以减小特定场景下对象拷贝的开销”
第一章 开始
“类型不仅定义了数据元素的内容,还定义了这类数据上可以进行的运算”
“
i
o
s
t
r
e
a
m
iostream
iostream库包含两个基础类型
i
s
t
r
e
a
m
istream
istream和
o
s
t
r
e
a
m
ostream
ostream,分别表示输入流和输出流”
流就是一个
o
b
j
e
c
t
object
object,流是介于数据和程序之间的一个中转设备,由于流的存在我们可以不需要直接操作数据,而是通过操作流的方式间接操作数据,流统一了我们对数据的操作标准。
“程序员常常在调试时添加打印语句,这类语句应该保证一直刷新流,否则,如果程序崩溃,输出可能还留在缓存区内,从而导致关于程序崩溃位置的错误判断”
”当我们使用一个 i s t r e a m istream istream对象作为条件时,其效果是检测流的状态。当遇到文件结束符,或遇到一个无效输入时, i s t r e a m istream istream的状态会变为无效。处于无效状态的 i s t r e a m istream istream对象会使条件变为假“
“
#
i
n
c
l
u
d
e
\#include
#include指令,包含来自标准库的头文件时,应该用尖括号包围头文件名,对于不属于标准库的头文件,则用双引号包围”
M
i
c
r
o
s
o
f
t
Microsoft
Microsoft 对 C++ 标准库的实现通常称为 STL 或标准模板库。
“大多数操作系统支持文件重定向,这种机制允许我们将标准输入与标准输出和命名文件关联起来”
第二章 变量和基本类型
算术类型
类型 | 最小尺寸 |
---|---|
short | 16位 |
int | 16位 |
long | 32位 |
long long | 64位 |
float | 6位有效数字 |
double | 10位有效数字 |
long double | 10位有效数字 |
这里的最小尺寸说法是出于不同系统整型等大小不同,无法严格确定
“使用 i n t int int执行整数运算,在实际应用中, s h o r t short short常常显得太小而 l o n g long long一般和 i n t int int有着相同的尺寸,如果你的数值超过了 i n t int int的表示范围,选用 l o n g long long l o n g long long”
“在算数表达式中不要使用
c
h
a
r
char
char和
b
o
o
l
bool
bool,因为类型
c
h
a
r
char
char在一些机器上是有符号的,而在一些机器中又是无符号的”
大部分体系结构上,
c
h
a
r
char
char是默认带符号的,但是在一些特殊情况,例如ARM体系中,
c
h
a
r
char
char是不带符号的。
“执行浮点型运算选用 d o u b l e double double,这是因为 f l o a t float float常常精度不够而且两者计算代价相差无几”
“当我们赋给带符号类型一个超出它范围的值时,结果是未定义的,此时,程序可能继续工作,可能崩溃,可能生成垃圾数据”
“无法预知的行为源于编译器无须检测(有时是不能)的错误”
“当一个算数表达式中既有无符号数又有 i n t int int时,那个 i n t int int值会被转成无符号数”
“切勿混用无符号类型与带符号类型,如果表达式里既有有符号类型又有无符号类型,当带符号类型取值为负时会产生异常,这是因为带符号数会被转成无符号数”
“字符串字面值的类型实际上是由常量字符组成的数组,编译器在每个字符串字面值的结尾加上一个空字符”
“通过添加前缀和后缀,可以改变整型,浮点型,字符串类型的默认类型”
“变量提供一个具名的,可供程序操作的内存空间,C++中的每个变量都有数据类型,数据类型决定着变量所占的内存空间的大小与布局方式,该空间所能存储的值范围,以及变量能参与的运算”
“初始化不是赋值,初始化的含义是创建变量时赋予一个初始值,而赋值的含义是将当前的值擦去,用一个新值代替”
“定义在函数体内部的内置类型将不被初始化”
考虑到成本问题,如果要初始化的话就需要调用函数代码
“为了允许把程序拆成多个逻辑来编写,C++支持分离式编译,该机制允许程序分割为若干个文件,每个文件独立编译”
“C++语言是一种静态类型语言,意味着在编译阶段检查类型”
“面对一条比较复杂的指针或引用的声明语句,从右向左阅读有助于搞清楚它的含义”
//总结const的用法与细节
“一般来说,如果你认定一个变量是常量表达式那就把它声名成 c o n s t e x p r constexpr constexpr类型”
类型别名: t y p e d e f typedef typedef或 u s i n g using using
d e l c t y p e delctype delctype:希望从表达式的类型推断出想要定义的变量的类型,但是不想用该表达式的值初始化变量
第三章 字符串 向量和数组·
“如果一条表达式已经有了 s i z e ( ) size() size()函数就不要使用 i n t int int了,这样可以避免混用 u n s i g n e d unsigned unsigned和 i n t int int的后果”
“当把 s t r i n g string string对象和字符字面值与字符串字面值在一条表达式中混用的时候,必须确保每个加法运算符的两侧至少有一个 s t r i n g string string对象”
“因为某些历史原因,也为了和C兼容,所以C++语言的字符串字面值并不是标准库 s t r i n g string string的对象,切记, s t r i n g string string和字符串字面值是不同类型”
c c t y p e cctype cctype头文件中的函数
“如果循环体内部包含有向 v e c t o r vector vector对象添加元素的语句,则不能使用范围 f o r for for循环”
“如果容器为空,则 b e g i n begin begin和 e n d end end返回的都是尾后迭代器”
“C++程序员习惯性使用 ! = != !=,其原因和他们习惯使用迭代器而非下标一样,因为这种编程风格对标准库里的所有容器都适用”
“每个容器类定义了一个名为 i t e r a t o r iterator iterator的类型,该类型支持迭代器类型所规定的一套操作”
“任何一个改变 v e c t o r vector vector容量的操作,比如 p u s h _ b a c k push\_back push_back,都会使得该对象的迭代器失效”
“要想理解数组声名的含义,最好的方法是从数组的名字开始从内往外的顺序读”
“内置的下标运算不是无符号值类型,这一点和 v e c t o r vector vector与 s t r i n g string string不一样”
第四章 表达式
“当一个对象被用作是右值时,用的是对象的值(内容),当一个对象被用作是左值的时候,用的是对象的身份(在内存里的位置)”
“运算对象的求值顺序与优先律与结合律无关”
“除非必须,否则不用使用递增递减运算符的后置版本,因为后置版本需要把原始值存储下来以便于返回这个未修改的值如果我们不需要修改前的值,那么这个操作就是一种浪费”
“后置递增递减运算符的优先级高于解引用运算符”
c
o
n
d
i
t
i
o
n
?
e
x
p
r
e
s
s
i
o
n
1
:
e
x
p
r
e
s
i
o
n
2
;
condition \ ?\ expression1\ :expresion2\ ;
condition ? expression1 :expresion2 ;
习惯在日常的代码书写中用条件运算符简化代码
位运算符
“对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果舍弃,逗号运算符真正的结果是运算符右侧的表达式”
强制类型转换
运算优先级表 P 147 P_{147} P147
第五章 语句
“悬垂 e l s e else else问题,就C++而言,它规定 e l s e else else与离它最近的 i f if if配对,从而消除二义性”
异常处理机制
“程序的异常检测部分通过 t h r o w throw throw表达式引出一个异常”
t
r
y
try
try语句块
第六章 函数
“实参是形参的初始值,尽管实参和形参存在对应关系,但是并没有规定实参的求值顺序”
“名字的作用域是程序文本的一部分,名字在其中可见,对象的生命周期是在程序执行过程中该对象存在的一段时间”
“局部静态对象:有些时候有必要令局部变量的生命周期贯穿函数调用及之后的时间,可以将局部变量定义为 s t a t i c static static类型从而获得这样的对象”
分离式编译
引用 ⟺ \iff ⟺避免拷贝 o r or or返回额外信息
顶层 c o n s t const const对象和底层 c o n s t const const对象?
“把函数不会改变的形参定义成(普通的)引用是一种比较常见的错误,这么做带给函数的调用者一种误导,即函数可以修改它的实参的值”
这提示我们尽量使用常量引用
数组形参?
“管理数组实参的第二种技术是传递指向数组首元素和尾后元素的指针”
传递多维数组?
m a i n main main处理命令行选项?
含有可变形参的函数:无法提前预知应该向函数传递几个实参
“如果所有的实参类型相同,可以传递一个名为
i
n
i
t
i
a
l
i
z
e
r
_
l
i
s
t
initializer\_list
initializer_list的标准库类型”
“如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板”
省略符形参
“在含有 r e t u r n return return语句的循环后面应该也有一条 r e t u r n return return语句,如果没有的话该程序就是错误的,很多编译器都无法发现此类错误”
“返回一个值的方式和初始化一个变量或形参的方式完全一样,返回的值用于初始化调用点的一个临时量,该临时量就是函数调用的结果”
引用返回左值
“函数的返回类型决定函数调用是否是左值,调用一个返回引用的函数得到左值,其他返回类型得到右值”
“特别是,我们能为返回类型是非常量引用的函数的结果赋值”
“C++11新标准规定,函数可以返回花括号包围的值的列表”
“因为数组不能被拷贝,所以函数不能返回数组”
声名一个返回数组指针的函数?
使用尾置返回类型?
“如果同一作用域内的几个函数名字相同但是形参列表不同,我们称之为重载函数,当调用这些函数时,编译器会根据传递的实参类型推断想要的是哪个函数”
“不允许两个函数除了返回类型外的其他所有要素都相同”
c o n s t const const形参?
c o n s t _ c a s t const\_cast const_cast和重载?
“一般来说,将函数声名置于局部作用域内不是一个明智的选择”
“当我们调用函数时,编译器首先寻找对该函数名的声名,一旦在当前作用域中找到了所需的名字,编译器就会忽略掉外层作用域中的·同名实体”
“在C++语言中,名字查找发生在类型检查前”
默认实参
“内联函数可避免函数调用的开销,将函数指定为内联函数,通常就是将它在每个调用点上内联地展开”
“ c o n s t e x p r constexpr constexpr函数是指能用于常量表达式的函数”
a
s
s
e
r
t
assert
assert预处理宏?
NDEBUG预处理变量?
函数匹配?
“调用重载函数时应尽量避免强制类型转换,如果在实际应用中确实需要强制类型转换,则说明我们设计的形参不合理”
最佳匹配编译器将实参类型到形参类型的转换等级?
“函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型由它的返回类型和形参类型共同确定,与函数名无关”
“当我们把函数作为一个值使用时,该函数自动地转换成指针,此外,我们还能直接使用指向函数的指针调用函数,无须提前解引用指针”
“在指向不同函数类型的指针间不存在转换规则”
重载函数指针?
第七章 类
“主要关注数据抽象的重要性,数据抽象能帮助我们将对象的具体实现与对象所能执行的操作分离开来”
“类的基本思想是数据抽象和封装,数据抽象是一种依赖于接口和实现分离的编程技术”
“成员函数的声名必须在类的内部,它的定义既可以在类的内部也可以在类的外部,作为接口组成部分的非成员函数,它们的定义和声名都在类的外部”
“定义在类内部的函数是隐式的 i n l i n e inline inline函数”
“成员函数通过一个名为 t h i s this this的额外的隐式参数来访问调用它的那个对象,当我们调用一个成员函数时,用请求该函数的对象地址初始化 t h i s this this”
“实际上,任何自定义名为 t h i s this this的参数或变量的行为都是非法的,我们可以在成员函数体内部使用 t h i s this this”
“常量对象,以及常量对象的引用或指针都只能调用常量成员函数”?
定义一个返回 t h i s this this对象的函数?
”一般来说,如果非成员函数是类接口的组成部分,则这些函数的声名应该与类在同一个头文件“
”默认情况下,拷贝类的对象其实拷贝的是对象的数据成员“
”每个类都分别定义了它的对象被初始化的方式,类通过一个或几个特殊的成员函数控制其对象的初始化过程,这些函数叫做构造函数。构造函数的任务是初始化类对象的数据成员,无论何时只要类的对象被创建,就会执行构造函数“
”类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数“
”只有当类没有声名任何构造函数时,编译器才会自动地生成默认构造函数“
”如果类包含有内置类型或者复合类型的成员,则只有当这些成员全都被赋予了类内的初始值时,这个类才适合使用于使用合成的默认构造函数“
”编译器创建的构造函数又被称为合成的默认构造函数“
= d e f a u l t default default的含义
构造函数初始值列表?
“尽管编译器能替我们合成拷贝,赋值和销毁的操作,但是必须要清楚的一点是,对于某些类来说合成的版本无法正常工作”
“在C++语言中,我们使用访问说明符加强类的封装性”
使用 c l a s s class class或者 s t r u c t struct struct关键字的区别?默认访问权限
“类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元”
“一般来说,最好在类定义开始或结束前的位置集中声名友元”
“封装的优点:1.确保用户代码不会无意间破坏封装对象的状态 2.被封装的类的具体实现细节可以随时改变,而无须调整用户级别的代码”
“友元的声名仅仅指定了访问的权限,而非一个通常意义上的函数声名,如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声名之外再专门对函数进行一次声名”
“许多编译器并未强制限定友元函数必须再使用之前在类的外部声名”
从 c o n s t const const成员函数返回 ∗ t h i s *this ∗this?
“一个 c o n s t const const成员函数如果以引用的形式返回 ∗ t h i s *this ∗this,那么它的返回类型将是常量引用”
基于 c o n s t const const的重载?
“每个类定义了唯一的类型,对于两个类来说,即使它们的成员完全一样,这两个类也是两个不同的类型”
“尽管重载函数的名字相同,但它们仍然是不同的函数。因此,如果一个类想要把一组重载函数声名成它的友元,它需要对这组函数中的每一个分别声名”
“类和非成员函数的声名不是必须在它们的友元声名之前”
“一个类就是一个作用域的事实能够很好地解释为什么当我们在类的外部定义成员函数时必须同时提供类名和函数1名”
“编译器处理完类中的全部声明之后才会处理成员函数的定义”
“然而在类中,如果成员使用了外层作用域中的某个名字,而该名字代表一个类型,则类不能在之后重新定义该名字”
“类型名的定义通常出现在类的开始处,这样就能确保所有使用该类型的成员都出现在类名的定义之后”
“尽管类的成员被隐藏了,但我们仍然可以通过加上类的名字或显式地使用 t h i s this this指针来强制访问成员”
建议使用构造函数初始值
“在很多类中,初始化和赋值的区别事关底层效率问题,前者直接初始化数据成员,后者则先初始化再赋值”
“最好令构造函数初始值的顺序与成员声明的顺序保持一致,而且如果可能的话,尽量避免使用某些成员初始化某些成员”
“如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数”
“一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些(或全部)职责委托给了其他构造函数”
默认构造函数?
转换构造函数?
“能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则”
e x p l i c i t explicit explicit构造函数只能用于直接初始化
聚合类?
类的静态成员?
“有的时候类需要它的一些成员与类本身直接相关,而不是与类的各个对象保持关联”
“和类的所有成员一样,当我们指向类外部的静态成员时,必须指明成员所属的类名, s t a t i c static static关键字则只出现在类内部的声名语句中”
第八章 IO库
“到目前为止,我们已经使用过的IO类型和对象都是操纵 c h a r char char类型的”
“概念上,设备类型和字符大小都不会影响我们要执行的IO操作”
“标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制实现的”
“简单地说,继承机制使我们可以声明一个特定的类继承自另一个类”
“我们不能拷贝或者对IO对象赋值,由于不能拷贝IO对象,因此我们也不能将形参或者返回类型设置为流类型”
“进行IO操作的函数通常以引用方式传递和返回流”
“读写一个IO对象会改变其状态,因此传递和返回的引用不能是 c o n s t const const的”
条件状态?
“确定一个流对象的状态最简单的方法就是将它当作一个条件来使用”
查询流的状态?
“每个输出流都管理一个缓冲区,用来保存程序读写的数据”
导致缓冲刷新的原因?
刷新缓冲区操作符: e n d l endl endl, f l u s h flush flush, e n d s ends ends
“如果想在每次输出操作后都刷新缓冲区,我们可以使用 u n i t b u f unitbuf unitbuf操纵符”
cout << unitbuf;
cout << nounitbuf;
“如果程序崩溃,输出缓冲区不会被刷新。当一个程序崩溃后,它所输出的数据很可能停留在输出缓冲区中等待打印”
“当一个输入流被关联到一个输出流时,任何试图从输入流读取数据的操作都会先刷新关联的输出流”
i
f
s
t
r
e
a
m
ifstream
ifstream 从一个给定文件读取数据
o
f
s
t
r
e
a
m
ofstream
ofstream 向一个给定文件写入数据
f
s
t
r
e
a
m
fstream
fstream 可以读写给定文件
“当一个 f s t r e a m fstream fstream对象被销毁时, c l o s e close close会自动被调用”
文件模式?
“以 o u t out out模式打开文件会丢弃已有数据”
“每次调用 o p e n open open时都会确定文件模式”
“在每次打开文件时,都要设置文件模式,可能是显式地设置,也可能是隐式地设置。当程序未指定模式时,就使用默认值”
“ s s t r e a m sstream sstream头文件定义了三个类型来支持内存IO,这些类型可以向 s t r i n g string string写入数据,从 s t r i n g string string读取数据,就像 s t r i n g string string是一个IO流一样”
i
s
t
r
i
n
g
s
t
r
e
a
m
istringstream
istringstream
o
s
t
r
i
n
g
s
t
r
e
a
m
ostringstream
ostringstream
s
t
r
i
n
g
s
t
r
e
a
m
stringstream
stringstream
“C++使用标准库类来处理面向流的输入和输出: i o s t r e a m iostream iostream处理控制台IO, f s t r e a m fstream fstream处理命名文件IO, s t r i n g s t r e a m stringstream stringstream完成内存 s t r i n g string string的IO”
第九章 顺序容器
“所有容器类都共享公共的接口,不同容器按不同方式对其进行扩展”
“一个容器就是一些特定类型对象的集合”
“顺序容器为程序员提供了控制元素存储和访问顺序的能力”
v
e
c
t
o
r
vector
vector:快速随机访问,在尾部之外的位置插入或删除元素可能很慢
d
e
q
u
e
deque
deque:支持快速随机访问,在头尾位子插入/删除速度很快
l
i
s
t
list
list:只支持双向顺序访问,在任何位置进行插入/删除操作速度都很快
f
o
r
w
a
r
d
l
i
s
t
forward_list
forwardlist:只支持单向顺序访问,在链表任何位置进行插入/删除操作速度都很快
a
r
r
a
y
array
array:固定大小数组,支持快速随机访问,不能添加或删除元素
s
t
r
i
n
g
string
string:与
v
e
c
t
o
r
vector
vector相似
“ v e c t o r vector vector将元素保存在连续的内存空间中,由于元素是连续存储的,元素的下标来计算其地址是非常快速的”
“ l i s t list list和 f o r w a r d l i s t forward_list forwardlist两个容器的设计目的是令容器任何位置的1添加和删除操作很快速。作为代价,这两个容器不支持元素的随机访问:为了访问一个元素,我们只能遍历整个容器”
“ f o r w a r d l i s t forward_list forwardlist和 a r r a y array array是新C++标准添加的类型”
“与内置数组相比, a r r a y array array是一种更安全更容易使用的数组类型”
“ f o r w a r d l i s t forward_list forwardlist的设计目标是达到与最好的手写的单向链表数据结构相当的性能”
“新标准库容器的性能几乎肯定与最精心优化过的同类数据结构一样好(通常会更好),现代C++程序应该使用标准库容器,而不是更原始的数据结构”
“通常,使用 v e c t o r vector vector是最好的选择,除非你有很好的理由选择其他容器”
“如果你的程序有很多小的元素,且空间的额外开销很重要,则不要使用 l i s t list list或 f o r w a r d l i s t forward_list forwardlist”
“如果程序要求随机访问元素,应使用 v e c t o r vector vector或 d e q u e deque deque”
程序需要在容器中间插入或删除元素 ⟺ \iff ⟺ l i s t list list或 f o r w a r d l i s t forward_list forwardlist
容器类型上的操作?
“一般来说,每个容器都定义在一个头文件中,文件名与类型名相同”
“与容器一样,迭代器有着公共的接口:如果一个迭代器提供某个操作,那么所有提供相同操作的迭代器对这个操作的实现方式都是相同的”
迭代器范围?
反向迭代器?
“当不需要写访问时,应使用 c b e g i n cbegin cbegin和 c e n d cend cend”
容器的定义和初始化
“只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器”
“当我们用一个对象来初始化容器时,或将一个对象插入到容器中时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身”
“将元素插入到 v e c t o r vector vector, d e q u e deque deque, s t r i n g string string中的任何位置都是合法的,然而,这样做可能很耗时”
“ e m p l a c e emplace emplace函数在容器中直接构造元素,传递给 e m p l a c e emplace emplace函数的参数必须与元素类型的构造函数相匹配”
“对一个空容器调用 f r o n t front front和 b a c k back back,就像使用一个越界的下标,是一种严重的程序设计错误”
“在容器中访问元素的成员函数返回的都是引用,如果我们使用 a u t o auto auto变量来保存这些函数的返回值,并且希望使用此变量来改变元素的值,必须记得将变量定义为引用类型”
“删除元素的成员函数并不检查其参数,在删除元素之前,程序员必须确保它们是存在的”
f o r w a r d l i s t forward_list forwardlist中添加和删除操作的特殊性?
r e s i z e resize resize改变容器大小
“向容器中添加元素和从容器中删除元素的操作可能会使指向容器元素的指针,引用,迭代器失效”
“”由于向迭代器添加元素和从迭代器删除元素的代码可能会使迭代器失效,因此必须保证每次改变容器的操作之后都正确地重新定位迭代器“
” r e s e r v e reserve reserve并不改变容器中元素的数量,它仅影响 v e c t o r vector vector预先分配多大的内存空间“
”理解 c a p a c i t y capacity capacity和 s i z e size size的区别非常重要。容器的 s i z e size size是指它已经保存的元素的数量,而 c a p a c i t y capacity capacity则是在不分配新的内存空间的前提下它最多可以保存多少元素“
s t r i n g string string数值转换?
容器适配器
标准库定义了三个顺序容器适配器:
s
t
a
c
k
stack
stack,
q
u
e
u
e
queue
queue,
p
r
i
o
r
i
t
y
q
u
e
u
e
priority_queue
priorityqueue
“适配器是标准库的一个通用概念。容器,迭代器,函数都有适配器。本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型”
第十章 泛型算法
“标准库容器定义的操作集合惊人的小,标准库并未给每个容器添加大量功能,而是提供了一组算法,这些算法中的大多数都独立于任何特定的容器”
“大多数算法都定义在头文件 a l g o r i t h m algorithm algorithm中”
”泛型算法本身不会执行容器的操作,它们只会运行于迭代器之上,执行迭代器的操作“
”泛型算法运行于迭代器之上而不会执行容器操作的特性带来了一个令人惊讶但非常必要的编程假定,算法永远不会改变底层容器的大小。算法可能改变容器中保存的元素的值,也可能在容器内移动元素,但永远不会直接添加或删除元素“
”对于只读取而不改变元素的算法,通常最好使用 c b e g i n ( ) cbegin() cbegin()和 c e n d ( ) cend() cend(),但是,如果你计划使用算法返回的迭代器来改变元素的值,就需要使用 b e g i n ( ) begin() begin()和 e n d ( ) end() end()“
”那些只接受一个单一迭代器来表示第二个序列的算法,都假定第二个序列至少与第一个序列一样长“
”当我们使用将新值赋予序列中的元素的算法时必须注意确保序列原大小至少不小于我们要求算法写入的元素数目“
”一些算法从两个序列中读取元素,构成这两个序列的元素可以来自于不同类型的容器“
”向目的位置迭代器写入数据的算法假定目的位置足够大,能容纳要写入的元素“
插入迭代器?
L a m b d a Lambda Lambda表达式
“一个Lambda表达式表示一个可调用的代码单元,我们可以将其理解为一个未命名的内联函数”
“与任何函数类似,一个 L a m b d a Lambda Lambda具有一个返回类型,一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部”
“我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体”
“捕获列表只用与局部非 s t a t i c static static变量,lambda可以直接使用局部 s t a t i c static static变量和在它所在函数之外声名的名字”
“当我们定义一个lambda时,编译器生成一个与lambda对应的新的(未命名)类类型”
第十一章 关联容器
“关联容器和顺序容器有着根本的不同,关联容器中的元素是按关键字来保存和访问的,与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的”
第十二章 动态内存
“在C++中,内存通过new表达式分配,通过delete表达式释放的。标准库还定义了一个allocator类来分配动态内存块。分配动态内存的程序员应负责释放它所分配的内存,内存的正确·释放是非常容易出错的地方,要么内存永远不会被释放,要么在仍有指针引用它时就被释放了。新的标准库定义了智能指针类型。对于一块内存,当没有任何用户使用它时,智能指针会自动释放它。现代C++程序应尽可能使用智能指针”