<!-- @page { size: 21.59cm 27.94cm; margin: 2cm } P { margin-bottom: 0.21cm } -->
第 10 章 关联容器
关联容器: map<K,V>, set<K>, multimap<K,V>, multiset
10.1
pair类型:在 <utility>中, pair<T1,T2> p1,make_pair(v1,v2),<, ==, p.first, p.se
cond
10.3
在实际应用中, map的键类型唯一的约束就是必须定义 <操作符,而且该操作符应能“正确地工作”,这一点很重要
map定义的类型: map<K,V>::key_type, map<K,V>::mapped_type, map<K,V>::value_type
记住: map的接口中, value_type是 pair类型,它的键成员是 const map<K,V>::key_type类型,不能修改;值成员是 map<K,V>::mapped_type类型,可以修改
用下标访问 map:若访问的元素不存在,则将导致在 map容器中添加一个新的元素,它的键值即为该下标值
值初始化:类类型用默认构造函数初始化,内置类型初始化为 0
map的 insert操作: insert(e), insert(beg,end), insert(iter,e)
查找并读取 map中的元素: m.count(k), m.find(k)
count适用于解决判断 map容器中某键是否存在的问题,而 find适用于解决在 map容器中查找指定键对应的元素的问题
从 map对象中删除元素: m.erase(k), m.erase(p), m.erase(b,e)
10.4
set中的键也为 const,只能对其做读操作
10.5
multimap不支持下标运算,因为这类容器某个键可能对应多个值
在 multimap和 multiset容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。迭代遍历 multimap或 multiset容器时,可保证依次返回特定键所关联的所有元素
multimap和 multiset迭代器特殊操作: m.lower_bound(k),m.upper_bound(k), m.equal_range(k)
总结:
关联容器( map,set,multimap,multiset)的操作, pair类型,关联容器定义的类型别名
第 11 章 泛型算法
11.1
算法固有地独立于容器:泛型算法与容器类型无关,与容器中存储的元素类型也无关。算法对容器的一切操作都是用迭代器来完成,算法本身并不执行容器提供的操作
标准库提供了超过 100种算法,泛型算法在 <algorithm>中定义,泛化的算术算法在 <numeric>中定义
11.2
只读算法: find(beg,end,value),find_if(beg,end,testFunction), accumulate(beg,end,initval)
注意: accmulate在 <numeric>中,其第三个实参是必要的,因为返回的结果就是第三个实参指示的类型
若第三个实参是 string类型,则不能传递字符串字面值,因为字面值是 const char*类型,它不能做 string加法的左操作数(只可以做右操作数),而左操作数必须是 string类型
string对象与 char*对象可以相互比较
find_first_of(beg1,end1,beg2,end2)
写容器元素的算法:填充算法 fill(beg,end,value), fill_n(beg,length,value)
对指定数目的元素做写入运算,或者写到目标迭代器的算法,都不检查目标的大小是否足以存储要写入的元素
插入迭代器: back_insert,在 <iterator>头文件中,返回容器的迭代器,用于算法进行元素插入
写入到目标迭代器的算法:复制算法 copy(beg,end,iter)
算法的 _copy版本: replace(beg,end,orival,repval),replace_copy(beg,end,iter,oriv
al,repval)
排序算法: sort(beg,end), stable_sort(beg,end,cmpFunction)
删除相邻的重复元素: unique(beg,end),注意只是把不重复的元素前移了,并没有实际删除元素的空间,因为所有算法都不直接修改容器的大小。如果需要添加或删除元素,则必须使用容器操作
统计算法: count_if(beg,end,testFunction)
11.3
三种:插入迭代器、 iostream迭代器、反向迭代器,在 <iterator>中定义
插入迭代器: back_inserter,front_inserter,inserter
注意: front_inserter的使用将导致元素以相反的次序出现在目标对象中
流迭代器:有 istream_iterator,ostream_iterator,只定义了最基本的迭代器操作自增、解引用和赋值,还可比较 istream迭代器是否相等
istream_iterator<T> in(strm);
istream_iterator<T> in;
ostream_iterator<T> in(strm);
ostream_iterator<T> in(strm,delim);
流迭代器都是类模板:任何已定义输入操作符 >>的类型都可以定义 istream_iterator,任何已定义输出操作符 <<的类型也可定义 ostream_iterator。
流迭代器的限制:一旦 ostream_iterator对象赋了一个值,写入就提交了。赋值后,没有办再改变这个值; ostream_iterator没有 ->操作符
反向迭代器:所有反向迭器都提供成员函数 base(),转换成所指位置的正向迭代器
流迭代器不能创建反向迭代器
const容器的 begin(),end()等返回的是 const_iterator,因此不能用非 const的引用或指针指向它
五种迭代器:
输入迭代器: istream_iterator,支持的算法包括 find和 accumulate。支持操作符 ==, !=, ++, *, ->
输出迭代器: ostream_iterator,如 copy算法。支持操作符 ++, *
前向迭代器:如算法 replace。支持输入输出迭代器的所有操作
双向迭代器:如算法 reverse,所有标准库容器提供的迭代器都至少达到双向迭代器的要求。支持前向迭代器的所有操作,还有 --操作符。 map,set和 list类型提供双向迭代器
随机访问迭代器:如 sort算法。 string,vector,deque和访问数组元素的指针是随机访问迭代器。支持双向迭代器所有操作,还有 <, <=, >, >=; iter与 n的+, +=, -, -=操作; iter1与 iter2的减法操作;下标操作 iter[n]。
需要低级类别迭代器的地方,可使用任意一种更高级的迭代器
在处理算法时,最好将关联容器上的迭代器视为支持自减运算的输入迭代器,而不是完整的双向迭代器
算法分类:只读算法、赋值算法、将一个元素移给另一个元素的算法
11.5
list容器特有的操作: merge,remove,remove_if,reverse,sort,splice
与对应的泛型算法不同, list容器特有的操作参添加和删除元素
第 12 章 类
12.1
类的成员:可以是数据、函数或类型别名
在类内部,声明成员函数是必需的,而定义成员函数则是可选的。在类内部定义的函数默认为 inline
const成员函数不能改变其所操作的对象的数据成员
在类内部定义的成员函数,将自动作为 inline处理。可以在类内部声明 inline函数,在类外部定义它;或者类内部声明不是 inline,但在类外定义时用 inline。 inline成员函数的定义必须在调用该函数的每个源文件中是可见的。因此不在类内部定义的 inline成员函数,其定义通常应放在有类定义的同一头文件中
前向声明:只声明一个类而不定义它。
class Screen;
在声明之后,定义之前, Screen是一个不完全类型,只知它是一个类型,但不知道包含哪些成员。
在一个给定的源文件中,一个类只能被定义一次。如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的。因此将类定义放在头文件中,并使用头文件保护符是最好的。
不完全类型:不能定义该类型的对象;只能用于定义指向该类型的指针及引用,或者用声明(而不是定义)使用该类型合为形参类型或返回类型的函数。
12.2
非 const成员函数: this的类型是指向类类型的 const指针,可以改变 this所指向的值,但不能改变 this所保存的地址
const成员函数: this是指向 const类类型对象的 const指针,既不能改变指向的对象,也不能改变指针本身,因此返回时不能用非 const引用指向它,因此返回的必须是 const的类类型
基于 const的重载:基于成员函数是否为 const,可以重载一个成员函数,编译器会根据调用它的对象是否为 const的来选择一个版本;同样,基于指针或引用形参是否指向 const对象,可以重载一个函数,编译器会根据它指向的实参是否为 const的来选择一个版本。
可变数据成员:用 mutable声明,对象的可变数据成员在 const成员函数内也可以被修改
12.2
出现在类的定义体之外的成员定义必须指明成员出现在哪个类中,即成员名字要使用完全限定符,这时形参表和函数体就会处于类作用域中,不再需要完全限定符
函数返回类型出现在成员名字前面,因此不一定在类使用域中,必须使用完全限定名
12.4
构造函数不能指定返回类型,不能声明为 const
构造函数初始化列表只在构造函数的定义中而不是声明中指定
构造函数分两个阶段执行:初始化阶段;普通的计算阶段。不管是否显示指定初始化列表,类类型的数据成员总是在初始化阶段初始化的(不指定时会隐式地默认初始化)。构造函数体中的语句是在普通的计算阶段进行。
隐式地初始化阶段:即没有指定初始化列表。类类型使用默认构造函数初始化,内置或复合类型的局部成员不初始化,全局成员初始化为 0。因此类类型的数据成员总是在初始化阶段进行初始化
没有默认构造函数的类类型成员,以及 const或引用类型的成员,都必须用初始化列表初始化。因为 const或引用在构造函数体内对它们进行赋值是不合法的, const成员在定义时就要指定值,而不能再赋值,引用也要在定义时就要绑定到对象,否则没有指向怎么就能赋值呢
成员初始化的次序与成员定义的次序一致,而不是初始化列表中给出的次序。因此按照与成员声明一致的次序编写构造函数初始化列表是个好习惯
初始化式可以是任意表达式;类类型成员的初始化可以使用类的任一一个构造函数
构造函数中可以使用默认实参。为所有形参提供默认实参的构造函数同时也可以充当默认构造函数 (可见默认构造函数是可以有形参的,这时就必须提供默认实参)
一个类哪怕只定义了一个构造函数,编译器也不会再生成默认构造函数。
最佳实践:如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数,它应该定义自己的构造函数来初始化这些成员
使用默认构造函数:
Sales_item myobj(); //ok:但编译器认为这是函数声明,返回类型为 Sales_item
Sales_item myobj; //ok:使用了默认构造函数定义一个对象
Sale_item myojb=Sale_item(); //ok:使用默认构造函数创建一个无名对象并用来初始化 myobj
隐式类类型转换:可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。要抑制隐式转换,可以在构造函数前用 explicit声明
explicit只能用于类内部的构造函数声明上
最佳实践:一般单形参构造函数应该为 explicit。如果转换确实很有用,则用户可以显示地创建临时对象
类成员的显示初始化:对于没有定义构造函数并且全体数据成员均为 public的类,可以采用与初始化数组元素相同的方式初始化其成员。这种初始化形式是从 C继承过来的,很少使用。
12.5
友元:将一个类的非公有数据暴露给指定的函数或类
通常将友元声明成组地放在类定义的开始或结尾是个好习惯
友元可以是非成员函数,或之前定义的其他类的成员函数,或整个类,友元表示这些实体可以访问友元所在作用域的非公有数据
12.6
最佳实践:尽量少使用全局对象,而转用类的静态成员
类的 static数据成员:独立于该类的任意对象而存在,它只类关联,不与类的对象相关联
类的 static成员函数:没有 this形参,只能访问类的 static成员,不属于任何对象,因此不能声明为 const,不能声明为虚函数
static数据成员:可以为任意类型,常量、引用、数组、类类型等。 static数据成员不是通过类构造函数进行初始化的,因此应该在类外部定义并初始化(正好一次),但是 const static的整型常量可以在类内部初始化(仍然需要在类外部定义)
static数据成员的类型可以是其所属的类类型,而非 static成员只能是其所属类对象的指针或引用。 static数据成员可以用作默认实参,而非 static成员不能
总结:
内联函数( inline)、前向声明(不完全类型)、隐含的 this指针、可变数据成员( mutable)、构造函数、构造函数初始化列表(初始化次序)、默认构造函数、构造函数的隐式类类型转换、显示构造函数( explicit)、友元( friend)、 static数据成员、 static成员函数