标准模板类和容器
35、标准模板类(STL)(一),综述、容器及其操作
2010-03-26 16:45:34| 分类: C++STL|举报|字号订阅
C++的STL 是一个功能强大的库,它是建立在模板机制上,能够满足用户对存储管理不同类型数据的通用容器和施加在这些容器上的通用算法的巨大需求,并且具有完全的可移植性。因此在寻求程序的解决方案时,应该首先在STL 中寻求恰当的容器和算法。
STL 是一个通用性极高的编程工具,这种通用性不仅表现在可以使用通用的容器存储和管理任意类型的数据,更重要的是可以对不同的容器施加统一通用的算法和操作。实现这种通用性的关键思想就是:通过引进一个间接层对象对不同结构的数据容器进行统一的访问操作,从而简化了对容器的操作,使得实现操作的算法和函数通用化。这种思想是STL 的设计原则之一,也是软件设计中一个重要设计思想。在STL 中对容器访问的简化和独立就是通过循环子实现的,循环子可以无须依据某种特定容器的数据结构而完成对容器元素的访问,从而使得数据的存储结构与施加于数据的操作相互独立。标准模板库STL 是由容器类模板,用于访问这些容器的循环子类模板和可以通过循环子在这些容器上实现的各种算法类模板以及函数类模板组成的。STL 为这种标准算法和函数(包括用户定义的函数)借助循环子在容器上实现的应用建立了统一的规则。
容器:可容纳各种数据类型的数据结构。
迭代器:可依次存取容器中元素的东西,连接容器和算法
算法:用来操作容器中的元素的函数模板。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象。
函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用。
比如,数组int array[100]就是个容器,而int * 类型的指针变量就可以作为迭代器,可以为这个容器编写一个排序的算法
一、容器
(一)容器综述
标准库中定义了两种标准的容器:
1、序列容器(Sequence):向量vector、链表list 和双向队列deque。vector:后部插入/删除,直接访问
deque:前/后部插入/删除,直接访问
list:双向链表,任意位置插入/删除
2、关联容器(Associative container)
映射map、multimap 和集合set、multiset。
set:快速查找,无重复元素
multiset :快速查找,可有重复元素
map:一对一映射,无重复元素,基于关键字查找
multimap :一对一映射,可有重复元素,基于关键字查找
前两种合称为第一类容器。
3、序列转接器容器(又名容器适配器)(Sequence Adapter)
是一种可以改变函数对象或容器接口的组件。
序列转接器:堆栈stack、队列queue 和优先级队列priority_queue。
s tack:LIFO
queue:FIFO
priority_queue:优先级高的元素先出
对象被插入容器中时,被插入的是对象的一个复制品。
4、预定义数组、字符串类string、数值数组类valarray 和位集合bitset 均可以视为容器,但不是标准容器。标准容器的成员绝大部分都具有共同的成员名。,(1)类型成员
value_type元素类型
allocator_type内存管理器类型
size_type下标,元素计数类型
difference_type循环子之间差的类型
iterator循环子,相当于value_type*
const_iterator常循环子,相当于const value_type*
reverse_iterator逆序循环子,相当于value_type*
const_reverse_iterator常逆序循环子,相当于const value_type* reference元素引用,相当于value_type&
const_reference元素常引用,相当于const value_type&
key_type关键字类型(只用于关联包容器)
mapped_type映射值类型(只用于关联包容器)
key_compare比较标准类型(只用于关联包容器)
begin()指向第一个元素
end()指向最后一个元素的后一个位置
rbegin()指向逆序的第一个元素
rend()指向逆序的最后一个元素的后一个位置
front()访问第一个元素
back()访问最后一个元素
[]无测试的下标访问(不用于list)
at()有测试的下标访问(只用于vector和deque)
f ront():返回容器中第一个元素的引用,back():返回容器中最后一个元素的引用。比如,查list::front 的help,得到的定义是:
reference front();
const_reference front() const;
list有两个front函数
reference 和const_reference 是typedef的类型
对于list
list
list
对于list
list
list
insert(p,x)将元素x加入到p之前
insert(p,n,x)在p之前加入n个x的拷贝
insert(p,first,last)在p之前加入区间[first,last)中的序列
erase(p)删除p处的元素
erase(first,last)删除区间[first,last)中的序列
clear()清除所有的元素
size()获取元素个数
empty()测试包容器是否为空
max_size()最大可能的包容器的大小
capacity()为向量包容器分配的空间(只用于vector)reserve()为扩充向量包容器保留空间(只用于vector)resize()改变包容器的大小(只用于vector,list和deque)swap()交换两个包容器中的元素
get_allocator()获取包容器的内存管理器的副本
==测试两个包容器的内容是否相等
!=测试两个包容器的内容是否不同
<测试一个包容器是否在另一个包容器字典序之前
container()构造一个空容器
container(n)用缺省值构造n个元素的容器(不能用于关联包容器)container(n,x)构造具有n个x拷贝的容器(不能用于关联包容器)container(first,last)初始化容器区间[first,last)上的元素
container(x)容器拷贝构造函数
~container()析构容器和它的全部元素
operator=(x)从容器中x元素拷贝分配
assign(n,x)为容器分配n个x的拷贝(不用于关联容器)assign(first,last)在容器的[first,last)区间中分配
③O(n):表示容器此项操作的时间复杂度与容器中元素个数n的线性相关。
④O(n)+:表示容器此项操作的时间复杂度与容器中元素个数n线性相关并且还需要增加一些附加操作时间花费。
⑤O(log(n)):表示容器此项操作的时间复杂度与容器中元素个数n对数相关。
⑥O(log(n))+:表示容器此项操作的时间复杂度与容器中元素个数n对数相关并且还需要增加一些附加操作时间花费。
⑦Ran:表示容器对应的循环子为随机访问循环子。
⑧Bi:表示容器对应的循环子为双向访问循环子。
C
(三)容器的比较和对比和总结
1、若两容器长度相同、所有元素相等,则两个容器就相等,否则为不等。若两容器长度不同,但较短容器中所有元素都等于较长容器中对应的元素,则较短容器小于另一个容器;若两个容器均不是对方的子序列,则取决于所比较的第一个不等的元素。
#include
#include
int main()
{
std::vector
std::vector
v1.push_back (5);
v1.push_back (1);
v2.push_back (1);
v2.push_back (2);
v2.push_back (3);
std::cout << (v1 < v2);
return 0;
}
2、所有标准库容器共有的成员函数。
相当于按词典顺序比较两个容器大小的运算符:=, < , <= , > , >=, == , != empty : 判断容器中是否有元素
max_size: 容器中最多能装多少元素
size: 容器中元素个数
swap: 交换两个容器的内容
3、只在第一类容器中的函数:
begin 返回指向容器中第一个元素的迭代器
end 返回指向容器中最后一个元素后面的位置的迭代器
rbegin 返回指向容器中最后一个元素的迭代器
rend 返回指向容器中第一个元素前面的位置的迭代器
erase 从容器中删除一个或几个元素
clear 从容器中删除所有元素
4、迭代器
用于指向第一类容器中的元素。有const 和非const两种。
通过迭代器可以读取它指向的元素,通过非const迭代器还能修改其指向的元素。迭代器用法和指针类似。
定义一个容器类的迭代器的方法可以是:
容器类名::iterator 变量名;
或:容器类名::const_iterator 变量名;
访问一个迭代器指向的元素:* 迭代器变量名
迭代器上可以执行++ 操作, 以指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,则迭代器变成past-the-end值。
要使用一个past-the-end值的迭代器来访问对象是非法的,就好像使用NULL或未初始化的指针一样。例如:
#include
#include
using namespace std;
int main() {
vector
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
vector
for( i = v.begin();i != v.end();i ++ )
cout << * i << ",";
cout << endl;
vector
for( r = v.rbegin();r != v.rend();r++ )
cout << * r << ",";
cout << endl;
vector
for( j = v.begin();j != v.end();j ++ )
* j = 100;
for( i = v.begin();i != v.end();i++ )
cout << * i << ",";
}
输出结果:
1,2,3,4,
4,3,2,1,
100,100,100,100,
不同容器上支持的迭代器功能强弱有所不同。容器的迭代器的功能强弱,决定了该容器是否支持STL中的某种算法。
例1:只有第一类容器能用迭代器遍历。
例2:排序算法需要通过随机迭代器来访问容器中的元素,那么有的容器就不支持排序算法。
5、STL中的迭代器
STL 中的迭代器按功能由弱到强分为5种:
1. 输入:Input iterators 提供对数据的只读访问。
1. 输出:Output iterators 提供对数据的只写访问
2. 正向:Forward iterators 提供读写操作,并能一次一个地向前推进迭代器。
3. 双向:Bidirectional iterators提供读写操作,并能一次一个地向前和向后移动。
4. 随机访问:Random access iterators提供读写操作,并能在数据中随机移动。编号大的迭代器拥有编号小的迭代器的所有功能,能当作编号小的迭代器使用。
6、不同迭代器所能进行的操作(功能)
所有迭代器:++p, p ++
输入迭代器:* p, p = p1, p == p1 , p!= p1
输出迭代器:* p, p = p1
正向迭代器:上面全部
双向迭代器:上面全部,--p, p --,
随机访问迭代器:上面全部,以及:
p+= i, p -= i,
p + i:返回指向p 后面的第i个元素的迭代器
p - i:返回指向p 前面的第i个元素的迭代器
//间隔一个输出:
ii = v.begin();
while( ii < v.end()) {
cout << * ii;
ii = ii + 2;
}
而list 的迭代器是双向迭代器,所以以下代码可以:
list
list
for( ii = v.begin(); ii !=v.end ();ii ++ )
cout << * ii;
以下代码则不行:
for( ii = v.begin(); ii < v.end ();ii ++ )
cout << * ii;
//双向迭代器不支持<
for(int i = 0;i < v.size() ; i ++)
cout << v[i]; //双向迭代器不支持[]
所有的标准库容器都定义了相应的迭代器类型。迭代器对所有的容器都适用,现代C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素。
1 .iterator,const_iterator作用:遍历容器内的元素,并访问这些元素的值。iterator可以改元素值,但const_iterator不可改。跟C
的指针有点像
(容器均可以++iter,而vector还可以iter-n, iter+n,n为一整型,iter1-iter2:结果是difference_type类型,表两元素的距离.)
2 .const_iterator 对象可以用于const vector 或非const vector,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值.
3 .const iterator与const_iterator是不一样的:声明一个const iterator时,必须初始化它。一旦被初始化后,就不能改变它的值,它一旦被初始化后,只能用它来
改它指的元素,不能使它指向其他元素。(因此const iterator几乎没什么用途)
例vector
const vector
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
例:读入一段文本到vector 对象,每个单词存储为vector 中的一个元素。把vector 对象中每个单词转化为小写字母。输出vector 对象中转化后的元素,每八个单词为一行输出.
类的在常成员方法中访问标准容器成员时或者常量标准容器引用作为参数时必须使用 const_iterator,否则不能编译通过。
所有的标准库容器都定义了相应的迭代器类型。迭代器对所有的容器都适用,现代C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素。
1 .iterator,const_iterator作用:遍历容器内的元素,并访问这些元素的值。iterator可以改元素值,但const_iterator不可改。跟C 的指针有点像
(容器均可以++iter,而vector还可以iter-n, iter+n,n为一整型,iter1-iter2:结果是difference_type类型,表两元素的距离.)
2 .const_iterator 对象可以用于const vector 或非const vector,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值.
3 .const iterator与const_iterator是不一样的:声明一个const iterator时,必须初始化它。一旦被初始化后,就不能改变它的值,
它一旦被初始化后,只能用它来
改它指的元素,不能使它指向其他元素。(因此const iterator几乎没什么用途)
例vector
const vector
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
例:读入一段文本到vector 对象,每个单词存储为vector 中的一个元素。把vector 对象中每个单词转化为小写字母。输出vector 对象中转化后的元素,每八个单词为一行输出.
类的在常成员方法中访问标准容器成员时或者常量标准容器引用作为参数时必须使用 const_iterator,否则不能编译通过。
所有的标准库容器都定义了相应的迭代器类型。迭代器对所有的容器都适用,现代C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素。
1 .iterator,const_iterator作用:遍历容器内的元素,并访问这些元素的值。iterator可以改元素值,但const_iterator不可改。跟C 的指针有点像
(容器均可以++iter,而vector还可以iter-n, iter+n,n为一整型,iter1-iter2:结果是difference_type类型,表两元素的距离.)
2 .const_iterator 对象可以用于const vector 或非const vector,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值.
3 .const iterator与const_iterator是不一样的:声明一个const iterator时,必须初始化它。一旦被初始化后,就不能改变它的值,它一旦被初始化后,只能用它来
改它指的元素,不能使它指向其他元素。(因此const iterator几乎没什么用途)
例vector
const vector
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
例:读入一段文本到vector 对象,每个单词存储为vector 中的一个元素。把vector 对象中每个单词转化为小写字母。输出vector 对象中转化后的元素,每八个单词为一行输出.
类的在常成员方法中访问标准容器成员时或者常量标准容器引用作为参数时必须使用 const_iterator,否则不能编译通过。
STL中的常用的vector,map,set,Sort用法