??本文主要供笔者复习使用,所以很多地方写的很简略,还请见谅。
??STL主要由两种组件构成,一是容器,包括vector、list、set、map等类. ??另一组组件是用以操作这些容器的所谓泛型算法 包括find() sort() replace() merge()等. ??vector和list是所谓的顺序容器 我们主要在顺序容器上进行所谓的迭代操作. ??map和set是关联容器。关联容器可以让我们快速查找到容器中的元素值 ??map是一组key/value组合,key是关键字,用于查找,
??所谓set,其中仅含有key。我们对它进行查找操作。
??如果想建立一个索引表,我们需要减检查需要插入的key是否在set中,如果不在则插入。
??这些算法之所以称为泛型,是因为它们和它们想要操作的元素的类型无关。
??泛型算法系使用的是function template技术。
??我们想实现一个find函数,让他对任何类型的vector和arr都能生效
template <typename T>
T* find(const T* first, const T* last, const T& value)
{
assert(first && last);
while (first < last)
{
if (*first == value)
return first;
first++;
}
return 0;
}
??为了给vector也适配,需要vector中first和last的地址,我们封装成一个函数。
template <typename T>
inline T* begin(const vector<T> &vec)
{
return vec.empty() ? 0 : &vec[0];
}
template <typename T>
inline T* end(const vector<T>& vec)
{
return vec.empty() ? 0 : &vec[vec.size()];
}
??我们还想让这个find函数能够处理list类,但是不幸的是list类中的元素不是连续存储的,所以它的语法显著的和上面的find函数不同。
??但是C++提供了新的东西,在底层指针的行为之上提供一层抽象,取代程序原本的“指针直接操作”方式。我们把底层指针的处理通通放在此抽象层中,让用户无需直接面对指针操作。
3.1 了解泛型指针(Iterator)
??标准库提供了iterator class,这层抽象能够使的如果first和last都是iterator class object,iterator的百度翻译是迭代器,可以说是非常形象了,可以这样写:
while (first != last)
{
cout << *first << ' ';
++first;
}
??这就好像把first和last当做指针一样。 ??唯一的差别在于其运算符*、!=、++都是由iterator class内相关的操作符重载函数提供的。 ??对于list iterator来说,其递增函数会沿着list的指针前进到下一个元素,对vector iterator而言,其递增函数前进至下一个元素的方式,是将目前的地址加上一个元素的大小。
??如何取得iterator呢,每个标准容器都有一个begin()函数和一个end()函数,范围一个iterator指针,指向第一个元素和最后一个元素的下一个位置。
??如何定义iterator?
vector<string> svec;
vector<string>::iterator iter = svec.begin();
??改进find函数:
template <typename IteratorType, typename elemtype>
IteratorType find1(IteratorType first, IteratorType last,
const elemtype& value)
{
while (first != last)
{
if (*first == value)
return first;
++first;
}
return last;
}
int main()
{
const int size = 8;
int a[size] = { 1, 1, 2, 3, 5, 8, 13, 21 };
vector<int> ivec(a, a + size);
list<int> ilist(a, a + size);
int* p = find1(a, a + size, 1024);
if (p == a + size)
{
cout << "find fail\n";
}
vector<int>::iterator it;
it = find1(ivec.begin(), ivec.end(), 1024);
if (it == ivec.end())
{
cout << "vector find fail\n";
}
list<int>::iterator iter;
iter = find1(ilist.begin(), ilist.end(), 1024);
if (iter == ilist.end())
{
cout << "list find fail\n";
}
return 0;
}
3.2 所有容器的共同操作
以下为所有容器类(以及string类)的共同操作
- ==和!=运算符,返回true或false
- =运算符,将某个容器复制给另一个容器
- empty()容器内无元素时返回true 否则返回false
- size()返回容器内目前元素的个数
- clear()删除所有元素。
- begin()返回一个iterator,指向指向容器的第一个元素。
- end()返回一个iterator,指向容器最后一个元素的下一个位置。
- insert()将单一或某个范围内的元素插入容器内。
- erase()将容器内的单一元素或某个范围内的元素删除。
- insert()和erase()的行为视容器本身是顺序性容器还是关联容器而有所不同。
3.3 使用顺序性容器
??vector list deque
-
vector是一个动态数组 -
list是一个带头双向循环链表 -
deque双向队列
??deque对于最前端插入和最后端插入元素和删除元素效率较高。
??要使用顺序容器,首先要引头文件
#include <vector>
#include <list>
#include <deque>
定义顺序容器的方式
-
产生空的容器: list<string> slist;
vector<int> ivec;
-
产生特定大小的容器。每个元素都以其默认值为初值 list<int> ilist(32);
vector<double> dvec(24);
-
产生特定大小的容器,并为每个元素都指定初值。 vector<int> ivec(32,-1);
list<string> slist(24,"unassigned");
-
通过一堆iterator产生容器。这对iterator一般用来标示一整组作为初值的元素的范围。 int a[9] = { 1, 1, 2, 3, 5, 8, 13, 21 };
vector<int> ivec(a, a + 8);
-
根据某个已存在的容器产生新容器。 list<string> slist;
list<string> slist2(slist);
顺序容器的插入和删除
- push_back()
- push_front()
- pop_back()
- pop_front()
- front()
- back()
作用就不说了,在数据结构中都有提到。
??insert的四种用法(配合find使用)
-
iterator insert(iterator pos, elemtype val);
-
void insert(iterator pos, int count, elemtype value);
-
void insert(iterator1 pos, iterator2 first, iterator2 last);
-
iterator insert(iterator pos);
??erase的两种用法
-
iterator erase(iterator pos);
-
iterator erase(iterator first, iterator last);
返回的iterator指向被删除的最后一个元素的下一个元素的位置。 list不支持iterator的偏移运算,也就是说不能这样写 slist.erase(it1, it1 + num);
3.4 使用泛型算法
#include <algorithm>
四种泛型搜索算法:
- find()用于搜索无序集合里是否存在某值,搜索范围由iterator[first , last)标出。如果找到目标,find()会返回一个iterator指向该值,否则返回一个iterator指向last。
- binary_search()二分查找,搜索范围同上,如果找到目标返回一个true 找不到则返回false
- count()返回数值相符的元素数目。
- search()对比某个容器中是否存在某个子序列,如果存在则返回一个iterator指针指向子序列起始处;否则返回一个iterator指针指向容器末尾。
??函数对象实现了我们本来可能要以独立函数加以定义的事务,主要是为了效率。
-
六个算术运算符: plus<type> minus<type> negate<type>
multiplies<type> divides<type> modules<type>
-
六个关系运算符: less<type> less_equal<type> greater<type>
greater_equal<type> equal_to<type> not_equal_to<type>
-
三个逻辑运算符 logical_and<type> logical_or<type> logical_not<type>
使用它们要引头文件
#include <functional>
函数对象适配器
??函数对象适配器会对函数对象进行修改操作,binder adaper绑定适配器会将function object的参数绑定至某特定值,使二个操作数的函数对象转化为一个操作数的函数对象。
??标准库提供了两个binder adapter,bind1st会将指定值绑定至第一操作数,bind2nd则将指定值绑定至第二操作数。
??我们的需求是将用户的每个给定值与val进行比较,所以我们直接把less<int>与val绑定。
vector<int> filter(const vector<int>& vec, int val,
less<int>& lt)
{
vector<int> nvec;
vector<int>::const_iterator iter = vec.begin();
while ((iter = find_if(iter, vec.end(), bind2nd(lt, val)))
!= vec.end())
{
nvec.push_back(*iter);
iter++;
}
return nvec;
}
??接下来消除filter()与vector的元素类型,以及filter()与vector容器类型的依赖关系,来让filter()更加泛型化。
template <typename InputIterator, typename OutputIterator,
typename ElemType, typename Comp>
OutputIterator filter(InputIterator first,
InputIterator last, OutputIterator at,
ElemType val, Comp pred)
{
while ((first = find_if(first, last, bind2nd(pred, val)))
!= last)
{
cout << "found value :" << *first << endl;
*at++ = *first++;
}
return at;
}
int main()
{
const int size = 8;
int arr[size] = { 12,8,43,0,6,21,3,7 };
vector<int> vec(arr, arr + size);
int ret1[size];
vector<int> ret2(size);
cout << "test array :\n";
filter(arr, arr + size, ret1, 8, less<int>());
cout << "vector test :\n";
list<int> l(arr, arr + size);
list<int> ret3(size);
filter(vec.begin(), vec.end(), ret2.begin(),
8, greater<int>());
cout << "list test :\n";
filter(l.begin(), l.end(), ret3.begin(),
8, less_equal<int>());
}
??取反适配器negator,它会对函数对象的真伪值取反。 ??not1可以把一元操作符函数对象的真伪取反,not2可以把二元操作符的函数对象的真伪值取反。如果我们要找出大于等于10的元素,可以在while处这样修改:
while ((first = find_if(first, last,
not1(bind2nd(pred, val)))!= last)
??练习:写一个泛型算法,给一个线性容器,返回一个线性容器,这个容器里有所有不小于给定值val的值。
template <typename OutputIterator, typename InputIterator,
typename ElemType, typename cmp>
OutputIterator sub(InputIterator first, InputIterator last,
OutputIterator ret, ElemType val, cmp Cmp)
{
sort(first, last);
if ( find_if(first, last,
not1(bind2nd(Cmp, val))) != last)
{
InputIterator iter = find_if(first, last,
not1(bind2nd(Cmp, val)));
while (first != iter)
{
*ret++ = *first++;
}
}
return ret;
}
int main()
{
int arr[10] = { 5,6,8,9,4,3,2,1,10,7 };
vector<int> l(arr, arr + 10);
vector<int> ret(10);
sub(l.begin(), l.end(), ret.begin(), 5, less<int>());
return 0;
}
3.5 使用map
??map被定义为一对数值,其中key扮演索引的角色,通常是个字符串,另一个数值是value
#include <map>
map<string, int> words;
words["hello"] = 1;
words[tword]会取出tword对应的value
如果tword不在map内,它便会因此放到map中,并获得默认值0
??map对象有一个名为first的member,对应于key,有另一个名为second的member,对应于value。
??map的查找使用find函数,如果key已经在其中,会返回一个iterator,指向key/value形成的一个pair,反之返回end()。
??map的查找还可以使用count(),会返回某特定项在map内的个数。
??任何一个key值在map中最多只会有一份,如果需要存储多份相同的key值,就必须使用multimap。
3.6 使用Set
??Set由一群key组成。如果我们想知道某值是否存在于某个集合内,就可以使用set。
-
创建set set<ElemType> s1(iterator i1, iterator i2);
set<ElemType> s2(ElemType val);
-
插入set s1.insert(ival);
s2.insert(iterator1, iterator2);
3.7 使用Iterator Inserter
??直接使用泛型指针迭代可能容器空间不够会出问题,为此,STL提供了三个所谓的插入适配器,这些适配器让我们可以避免使用指针的运算符造成空间不够的情况。
-
back_inserter()
会以容器的push_back()函数取代运算符。对vector容器来说,这是比较合适的inserter,传入back_insert()的参数就是容器本身。 -
inserter()
会以容器的insert()函数取代运算符,inserter()函数接受两个参数,一个是容器,另一个是容器,另一个是一个iterator,指向容器内的插入操作的起点。 -
front_inserter()
会以容器的push_front()函数取代运算符,这个只适用于list和deque
要使用这些适配器,要引头文件
#include <iterator>
int main()
{
const int size = 8;
int arr[size] = { 12,8,43,0,6,21,3,7 };
vector<int> vec(arr, arr + size);
int ret1[size];
vector<int> ret2(size);
cout << "test array :\n";
filter(arr, arr + size, ret1, 8, less<int>());
cout << "vector test :\n";
list<int> l(arr, arr + size);
list<int> ret3(size);
filter(vec.begin(), vec.end(),
back_inserter(ret2), 8, greater<int>());
cout << "list test :\n";
filter(l.begin(), l.end(),
front_inserter(ret3), 8, less_equal<int>());
}
3.8 使用iostream Iterator
??C++提供了可以指向输入输出流的泛型指针 它的简单用法如下: ??思想和前面的iterator的使用一样,需要一个first和一个last来标记输入的范围。
istream_iterator<string> is(cin);
istream_iterator<string> eof;
vector<string> test;
copy(is, eof, back_inserter(test));
ostream_iterator<string> os(cout, " ");
??从记事本读入字符串,然后写到另一个记事本里:
ofstream out_file("output_file.txt", ios_base::app);
int main()
{
ifstream in_file("input_file.txt");
ofstream out_file("output_file.txt");
if (!in_file || !out_file)
{
cerr << "! 打开文件失败!\n";
return -1;
}
istream_iterator<string> is(in_file);
istream_iterator<string> eof;
vector<string> test;
copy(is, eof, back_inserter(test));
sort(test.begin(), test.end());
ostream_iterator<string> os(out_file, "\n");
copy(test.begin(), test.end(), os);
}
|