1 容器的共通能力
三个核心能力:
(1) 所有容器都提供value语意而非reference语意
(2) 总体而言所有元素形成一个次序
(3) 一般而言各项操作并非绝对安全
2容器的共通操作
每个容器类都提供一个default构造函数一个copy构造函数和一个析构函数
3 Vector容器
(1)Vector的赋值操作
C1=C2
C.assign(n,elem):复制n个elem赋值给C
C.assign(beg,end):将区间【beg,end】内的元素赋值给C
C.swap(C1);
Swap(c1,c2):全局
(2)元素存取
C.at(index): 返回索引index所标示的元素如果index越界抛出越界异常
C[index]:不检查越界
C.front():返回第一个元素。不检查第一个元素是否存在
C.Back():。。。。。。。。。。。。。。。
(3) 迭代器相关的函数:
C.begin(),C.end(),C.rbegin(),C.rend();
(4)安插和移除操作
迭代器必须指向一个合法的位置区间的起始位置不能在结束位置之后决不能从空容器中移除元素
下列移除操作性能会比较快
在容器尾部安插或移除元素
容器一开始就足够大
安插多个元素时调用一次比调用多次快
Vector的操作:
c.insert(pos,elem)
c.insert(pos,n,elem)
c.insert(pos,beg,end):在pos位置插入区间元素
c.push_back(elem);
c.pop_back()
c.erase(pos);
c.erase(beg,end);
c.resize(num):将元素数量改为num如果size()变大了多出来的元素多需要default构造函数完成
c.resize(num,elem)多出来的元素都是elem副本
c.clear()
(5)Classvector<bool>
C++ 标准程序库针对元素类别bool的vector设计了一个特殊版本目的是获取一个优化的vector,vector<bool>可能比一般的vector慢些,因为所有的元素操作都必须转化为bit操作
Vector<bool>的特殊操作
c.flip():将所有的bool元素取反值即求补码
m[index].flip():某一位取反
m[index]=value;
m[index]=m[index2];
4Deques容器
以下情形最好用deque容器:
(1) 你需要在两端安插元素
(2)无需引用容器内的元素
(3)要求容器释放不再使用的元素
Vector 和deques的接口几乎一样
以下数点与vector不同:
1 deques不提供容量操作(capacity和reserve)
1 deques 直接提供函数用以完成头部函数的安插和删除(push_front(),pop_front())
5 List容器
Lists与vector或deque的不同
(1) Lists不支持随机存取
(2) 任何位置上执行元素的安插和移除速度都很快
(3) 安插和删除元素动作并不会造成指向其它元素的各个pointer,reference,iterators失效
(4) Lists 对于异常有着这样的处理方式:要么操作成功要么什么都不发生
Lists的操作函数:
元素的存取:
c.front():返回第一个元素
c.back():返回最后一个元素
迭代器相关函数:
只有运用迭代器才能够存取list中的各个元素
元素的安插和移除lists提供deques的所有功能还增加了remove()和remove_if()算法应用于list身上的特殊版本
c.remove_if(op)移除所有造成op(elem)结果为true的元素
c.remove(value)移除所有其值为value的值
Lists的特殊变动性操作函数
含义区别:
size()指目前存在的元素数。 元素个数
capacity()指容器能存储数据的个数 容器容量
reserve()指定容器能存储数据的个数
resize() 重新指定有效元素的个数,区别与reserve()指定容量的大小
当创建空容器时,容量(capacity)为 0;当用完时,增加原容量的 1/2 (各编译器可能存在差异 vs2010是这样的,mingw则增加原容量)--适用如 vector这种元素连续存储的容器,如为list则不同。
capacity一般大于size的原因是为了避免每次增加数据时都要重新分配内存,所以一般会生成一个较大的空间,以便随后的数据插入。
6 Sets和Multisets
Sets不允许重复而Multisets允许重复
Set的排序准则:
(1) 必须是“反对称的”如果x<y为真则x>y为假
(2) 必须是可以传递的x<y,y<z则x<z
(3) 必须是非自反的 x<x永远为假
Set的结构为平衡二叉树自动排序的主要优点是具有良好的性能
Set和MultiSet不提供用来直接存取元素的操作函数
通过迭代器进行元素间的直接存取有一个限制:从迭代器的角度看元素的值是常数
6.2Sets和multisets的操作函数
Set c;
Set c(op)以op为准则产生一个空的set或multisets
Set C1(C2)
Set C(beg,end);
Set C(beg,end,op)
c.~set()
两种方式定义排序准则:
(1) 以template参数定义之例如:template<int,std::greater<int>>col
(2) 以构造函数参数定义之
非变动性操作:
和一般容器相似
特殊的搜索函数:
Count(elem):返回元素值为elem的元素个数
Find(elem)
Lower_bound(elem):返回elem第一个可以安置的位置也就是说元素值>=elem的第一个元素位置
Upper_bound(elem)
Equal_range(elem):返回elem可安插的第一个元素的位置和最后一个位置也就是元素值==elem的元素区间
Set 和Multisets的安插和移除:
安插函数的返回值型别不尽相同:
Set 提供如下接口:
Pair<iterator,bool>insert(const value_type&elem);
Iterator insert(iterator pos_hint,constvalue_type& elem);
Multisets提供如下接口:
Iterator insert(const value_type& elem);
Iterator insert(iterator pos_hint, constvalue_type& elem);
返回值型别不同的原因,multisets允许元素重复而sets不允许,set的返回值型别是以pair组织起来的两个值
在pair结构中的second成员表示安插是否成功
Pair结构中first成员返回新元素的位置。或返回现存的同值元素的位置,其他任何情况函数都返回新元素的位置
如果multset内含重复元素你不能使用erase()删除这些重复的元素你可以这么做
Set::multiset<elem>::iteratorpos;
Pos=coll.find(elem);
If(pos!=coll.end())
Coll.erase(pos);
序列式容器:
Iterator erase(iterator pos);
关联式容器:
Void erase(iterator pos)
7 Map和Multimaps
元素具备条件:
(1) key/Value 必须具备assignable(可赋值)和copyable(可复制)
(2) 对排序准则而言,key必须是compareable(可比较的)
7.1 Maps和Multimaps的能力
你不可以直接改变元素的key因为这会破坏正确的次序要修改元素的key你必须移除拥有该key的元素,然后插入拥有新的key/value的元素
7.2操作函数
两种方式定义排序准则:
(1) 以template参数定义
例如:std::map<folat,std::string,std::greater<folat>>coll
(2) 构造函数参数定义
详见例子
2非变动性操作同一般序列
2 Maps和Multimaps 的特殊搜索函数
Count(key)
Find(key)
Lower_bound(key)
Upper_bound(key)
Equal_range(key)
3 maps和Multimaps只支持所有容器提供的基本赋值操作:
C1=C2
C1.swap(C2)
Swap(c1,c2)
4 迭代器函数和元素存取
在map和multimap中,所有元素的key都被视为常数。因此元素的实质类型是pair<const key,T>.这个限制是为确保你不会因为变更元素的key而破坏以排好序的元素次序
如果你一定要改变元素的key只有一条路:以一个”value相同的元素”替换掉旧元素
Maps提供了一种非常方便的手法让你改变元素的key像下面这样
Coll[“new_key”]=coll[“old_key”];
Coll.erase(“old_key”);
5 元素的安插和移除
三个不同的方法将vlaue传入map
(1) 运用value_type
避免隐式类型转换你可以利用value_type明白传递正确的型别value_type是容器本身提供的类型定义
Std::map<syd::string,float>coll
Coll.insert(std::map<std::string,float>::value_type(“otto”,12.5));
(2) 运用pair<>
Coll.insert(std::pair<conststd::string,float>(“oto”,12.5));
(3) 运用make_pair();
Coll.insert(std::make_pair(“otto”,12.5));
移除“迭代器所指元素”的正确做法
For(pos=coll.begin();pos!=coll.end();)
If(pos->second==value)
Coll.erase(pos++)
Else
++pos
7.3 将Maps视为关联式数组
M[key]:返回一个reference指向键值为key的元素如果元素不存在则安插
STL其他容器
使你的容器STL化的三种不同方法
1The invasive approach(侵入性方法)
直接提供STL容器所需接口
2the noninvasive approach
由你拟写或提供特殊迭代器作为算法和特殊容器的界面
3the wrapper approach 包装法
将上述方法组合我们可以写一个外套类别来包装任何数据结构并显示出和STL容器相似的接口
7.4 各种容器的运行时机
容器的使用选择:
(1) 缺省情况下应该使用vector。
(2) 如果经常要在序列头部和尾部安插和移除元素应该采用deque
(3) 如果需要经常在容器的中段执行元素的安插,移除和移动可考虑使用list
(4) 如果你容器是这种性质“每次操作若不成功便无效”那么你可以用list
(5) 如果你经常需要根据某个准则来搜寻元素,那么应当使用“以该排序准则”对元素进行排序的set或者是multiset
(6) 如果处理key/value pair 请采用map/multimap(或者哈希表)
(7) 如果需要关联数组请使用map
(8) 如果需要字典结果请使用multimap
STL容器能力一览:
7.5 细说容器内的型别和成员
Container::reference
(1) 元素的引用型别典型定义container::value_type& 在vector<bool>中是个辅助类别
Container::size_type
无正负号整数型别,用于定义容器大小
Container::difference_type 有正负号用于定义距离
Container::key_compare 关联式容器的元素内的value型别
Contianer::allocator_type 配置器的型别
7.6 STL异常处理
七迭代器
7.1 迭代器的分类及能力
Input 迭代器 向前读取能力 供应者 istream
Output 迭代器 向前写入 ostream
Forward 迭代器 向前读取和写入
Bidlrectional 向前和向后读取和写入 容器
Random access 随机存取可读取也可写入
尽可能的优先选用前置式递增操作符++iter因为性能好
是否有效或写入动作是否成功你唯一做的就是写入写入在写入
Bidirectional 迭代器在forWard迭代器的基础上增加了回头遍历能力它支持递减运算符—iter ;iter—
Randomaccess 迭代器在bidirectional迭代器的基础上增加了随机存储能力
Vector迭代器的递增和递减
迭代器的递增和递减操作是个奇怪的问题一般而言你可以递增或递减暂时性的迭代器但是对已vector和string就不行。
Std::vector<int>coll
If(coll.size()>1)
Sort(++coll.begin(),coll.end());
通常编译sort()时就会失败如果用deque取代就没事因为vector的迭代器被实做一般的指针C++不允许你修改任何基本型别(包括指针)的暂时值但对于自定义类型没事
所以以上程序需改为:
Std::vector<int>::iteratorbeg=coll.begin();
7.2 迭代器相关辅助函数
C++标准库为迭代器提供三个辅助函数:advance(),distance()iter_swap()
前二者提供给所有迭代器一些原本只有Random access 才有的能力
7.2.1Advance()可令迭代器前进
Voidadvance(inputIterator&pos,Dist n)
使名为pos的Input的迭代器步进n个元素对Bidirectional迭代器很容Randomaccss而言n可以为负数代表后退
Advance()并不检查迭代器是否超过序列的end()所以调用advance()有可能导致未定义行为。
list<int>coll;
for(inti=1;i<=9;i++)
coll.push_back(i);
list<int>::iterator pos=coll.begin();
cout<<*pos<<endl;
advance(pos,3);
cout<<*pos<<endl;
advance(pos,-1);
cout<<*pos<<endl;
7.2.2distance()函数
Dist distance(inputiterator pos1,inputiterator pos2)
传回pos1和pos2之间的距离
两个迭代器必须指向同一个容器如果不是random access迭代器则从pos1开始往前走必须能够达到pos2亦即pos2的位置必须与pos1相同或其后
返回的dist型别由迭代器决定iterator_traits<inputiteraotr >::different
list<int>coll;
for(inti=-3;i<9;++i)
coll.push_back(i);
list<int>::iterator pos;
pos=find(coll.begin(),coll.end(),5);
if(pos!=coll.end())
cout<<"difference between bengin and 5"<<distance(coll.begin(),pos)<<endl;
7.2.3iter_swap()可交换迭代器所指元素的内容
Void iter_swap(ForwardIteratorpos1,Forwarditerator pos2)
交换迭代器pos1和pos2所指的值
迭代器的型别不必相同但其所指的两个值必须可以相互赋值
7.3 迭代器配接器
此特殊迭代器可以使得算法能够
以逆向模式(reverse)安插模式(insert mode)进行工作也可以和stream搭配工作
7.41Reverse 迭代器
Reverse迭代器是一种配接器重新定义递增和递减运算使其正好倒置算法将逆序处理元素
for_each(coll.rbegin(),coll.rend(),print);
rbegin()和rend()传回一个Reverse迭代器
你可以将一般iterator 转化为一个Reverse iterator但然原本的那个iterator必须具有双向移动能力转化前后迭代器的逻辑位置发生变化
方式转化:vector<int>::reverse_iterator rpos(pos);
以base()将逆向迭代器转化为正常迭代器逆向迭代器提供一个base()函数
7.3.2insert(安插型)迭代器
Insert迭代器用来将赋值新值操作转化为安插新值,通过这种迭代器算法可以执行安插行为而非覆盖行为
Insert迭代器通常使用下面两个实例技巧
(1) 被视作为一个无实际的动作只是简单传回*this所以对insert迭代器来说*pos与pos等价
(2) 赋值动作被转化为安插操作
所以对于一个insert迭代器你可以写pos=value也可写*pos=value
Insert迭代器的操作
*iter无实际操作传回iter
Iter=value安插value
++iter无实际操作
Iter++无实际操作
7.3.3Insert迭代器种类
Backinserter 类back_insert_iterator 调用push_back() 生成函数back_inserter(cont)
Frontinserter front_insert_iterator 调用push_front() front_inserter(cont)
Generalinserter insert_iterator insert(pos,value) inserter(cont,value)
Backinsert的实例:
vector<int>coll;
back_insert_iterator<vector<int>>iter(coll);
*iter=1;
iter++;
*iter=2;
iter++;
iter=3;
for_each(coll.begin(),coll.end(),print);
back_inserter(coll)=45;
back_inserter(coll)=46;
for_each(coll.begin(),coll.end(),print);
coll.reserve(2*coll.size());
copy(coll.begin(),coll.end(),back_inserter(coll));
for_each(coll.begin(),coll.end(),print);
注意:一定要在调用copy()函数之前确保有足够的空间因为在安插元素时可能会造成指向该vector的其他迭代器失效
7.3.3Stream流迭代器
Stream迭代器是一种迭代器配接器一个istream配接器可以从Input流中读取元素一个
Ostream迭代器的各项操作:
Ostream_iterator<T>(ostream)为ostream产生一个ostream迭代器
Ostream_iterator<T>(ostream,delim)各元素间以delim分割
*iter 无实际操作
Iter=value将value写到ostream像这样ostream<<value
++iter 无实际操作
Iter++ 无实际操作
对于istream还有:iter1==iter2 检查iter1和iter2是否相等
Iter1!=iter2
Istream_iterator<T>()产生一个end_of_stream 迭代器
Iter->number 传回先前读取的元素的成员
实例:
vector<int>coll;
back_insert_iterator<vector<int>>iter(coll);
istream_iterator<int>intreader(cin);
istream_iterator<int>intRederoff;
while(intreader!=intRederoff)
{
cout<<"once:"<<*intreader<<endl;
++intreader;
}
输入 1 2 3 f 4
结果:1 2 3
字符f的输入导致程序的结束是的由于格式错误stream不在出去good状态
7.5 迭代器的特性
7.5.1为迭代器编写泛型函数
通过迭代器特征你可以拟写这样的泛型函数:根据迭代器类的型别而派生型别定义或采取不同的实作码
(1) 运用迭代器型别
暂时变量可以声明如下:
Typenamestd::iterator_traits<T>::value_type tmp;
(2)运用迭代器类型
如果希望针对不同的迭代器类型采取不同的实作方案可以采取下面步骤
2.1让你的template函数将迭代器类型作为附加参数,调用另一个函数
Template<class iterator>
Inline void foo(iterator beg,iteratorend)
{
Foo(beg,end,std::iterator_traits<iterator>::iterator_category())
}
2.2针对不同的迭代器类型实作出上述所调用的函数。只有“并非派生自其它迭代器类型”的迭代器类型,才需要提供特殊化版本
Template<classBiIteraor>
Void foo(BiIteratorbeg,BiIterator)
例子distance()的实作
//general distance
7.4使用者自定义的迭代器
要求:
1) 提供必须要的五种型别定义就想iterator_traits结构中所描述的
Template<classT>
Structiterator_traits{
Typedef typename T::value_type value_type;
Typedef typename T::defference_type difference_type;
Typedef typename T::iterator_category iterator_category;
Typedef typename T::pointer pointer;
Typedef typename T::reference reference;
};
2) 提供一个iterator_traits结构
关于第一中C++标准库提供特殊基类iterator<>专门用来这类定义
Class my_iterator:public std::iterator<std::bidirectional_iterator_tag,type,std::ptrdiff_t,
Type*,type&>
{};
第一个迭代器类型就是iterator_traits所用的成员,最后三个是默认参数
关键实例:
template<classContainer>
classasso_insert_iteraotr:publicstd::iterator<std::output_iterator_tag,void,void,void,void>
{
protected:
Container& container;
public:
explicitasso_insert_iteraotr(Container&c):container(c)
{}
asso_insert_iteraotr<Container>&operator=(consttypenameContainer::value_type&value)
{
container.insert(value);
return*this;
}
asso_insert_iteraotr<Container>&operator*()
{
return*this;
}
asso_insert_iteraotr<Container>&operator++()
{
return*this;
}
asso_insert_iteraotr<Container>&operator++(int)
{
return*this;
}
};
template<classContainer>
inlineasso_insert_iteraotr<Container>asso_inserter(Container&c)
{
returnasso_insert_iteraotr<Container>(c);
}
void main()
{
set<int>coll;
asso_insert_iteraotr<set<int>>iter(coll);
*iter=1;
++iter;
*iter=2;
for_each(coll.begin(),coll.end(),print);
cout<<endl;
asso_inserter(coll)=44;
asso_inserter(coll)=55;
for_each(coll.begin(),coll.end(),print);
cout<<endl;
intvalues[]={33,6,4,12,-23,2};
copy(values,values+(sizeof(values)/sizeof(values[0])),asso_inserter(coll));
for_each(coll.begin(),coll.end(),print);
cout<<endl;