面向对象编程关注的是编程的数据方面,而泛型编程关注的是算法。
迭代器(iterator)
模板使得算法独立于储存的数据类型,而迭代器使算法独立于使用的容器类型。(算法参数变为迭代器类型,而不是各种类对象,函数对象,类型参数?)
迭代器不是某种类型,而是一系列的要求。可能需要设计一个迭代器类来满足需要达到的要求,可能只需要常规指针就能达到要求。(迭代器可以是指针,也可以是对象)
每个容器类(vector、list、deque等)定义了相应的迭代器类型。对于其中的某个类,迭代器可能是指针;而对于另一个类,则可能是对象。
STL文献使用术语概念(concept)来描述这一系列的要求。
概念可以具有类似继承的关系(输入出迭代器、正向迭代器、双向迭代器等),例如双向迭代器继承了正向迭代器的功能。然而,
不能将C++继承机制用于迭代器。例如,可以将正向迭代器实现为一个类,而将双向迭代器实现为一个常规指针。对C++而言,这种双向迭代器是一种内置类型(指针类型),不能从类派生而来。然而,从概念上来看,它确实能够继承。
有些STL文献使用术语改进(refinement)来辨识这种概念上的继承。
概念的具体实现被称为模型(model)。
迭代器是广义的指针,而指针满足所有的迭代器要求。
迭代器是STL算法的接口,而指针是迭代器,因此STL算法可以使用指针来对基于指针的非STL容器进行操作。
#include "iostream"
#include "algorithm"
using namespace std;
int main()
{
int arr[5] = {5,2,3,4,5};
sort(arr, arr + 5);
for (int x : arr)
cout << x << " ";
cout << endl;
return 0;
}
由于指针是迭代器,而算法是基于迭代器的,这使得STL算法可以用于常规数组。同样,可以将STL算法用于自己设计的数组形式,只要提供适当的迭代器(可以是指针,也可以是对象)和超尾指示器即可。
适配器(adapter)
适配器模式(Adapter Pattern)是一种补救模式,将一个类的接口转换成客户希望的另外一个接口,从而使原本由于接口不兼容而不能一起工作的类可以一起工作。
容器适配器:改变容器接口。
STL提供两个容器迭代器:queue和stack。它们都是修饰deque后成为另一种风貌的容器。
迭代器适配器:STL为某种迭代器提供了模板,该模板是迭代器概念的一个模型(概念的具体实现),它也是一个适配器(一个类或函数)。可以将一些其他接口转换为STL使用的接口。
有时,一个类将具有您所寻求的功能,但不具有访问该功能的正确接口。例如,STL copy()算法需要一对输入迭代器作为其前两个参数,以给出要复制的值范围。 istream对象可以充当此类数据值的源,但它没有复制算法可以使用的任何迭代器。
类似地,copy()算法的第三个参数是一个输出迭代器,它将复制的值定向到正确的目标。该目标可以是输出流,但输出流不直接提供任何输出迭代器。
通过“调整”要发送的消息来生成其他类对象想要接收的消息,适配器类就像“翻译器”一样。
有一个名为istream_iterator的迭代器适配器类,它提供了copy()算法对输入所期望的接口,并将来自此算法的请求转换为适当的istream操作。另外还有一个名为ostream_iterator,它提供了copy()算法对输出所期望的接口,并将来自算法的请求转换为适当的ostream操作。
#include "iostream"
#include "string"
#include "vector"
#include "iterator"
#include "algorithm"
using namespace std;
void Show(int n)
{
cout << n << " ";
}
int main()
{
int cast[5] = { 1,2,3,4,5 };
vector<int> dice(5);
copy(cast, cast + 5, dice.begin());
for(int x: dice)
cout << x << " ";
cout << endl;
vector<int>::iterator ri;
for (ri = dice.begin(); ri != dice.end(); ++ri)
cout << *ri << " ";
cout << endl;
ostream_iterator<int, char> out_iter(cout, " ");
copy(dice.begin(), dice.end(), out_iter); //将dice容器的整个区间复制到输出流
cout << endl;
vector<int>::reverse_iterator ro;
for (ro = dice.rbegin(); ro != dice.rend(); ++ro)
cout << *ro << " ";
cout << endl;
for_each(dice.rbegin(), dice.rend(), Show);
cout << endl;
return 0;
}
/*out_iter对象构造函数的第一个参数cout指出了要使用的输出流,它也可以是用于文件输出的流。
后一个字符串参数是在发送给输出流的每个数据项后显示的分隔符*/
copy()的前两个迭代器参数表示要复制的范围,最后一个迭代器参数表示要将第一个元素复制到什么位置。前两个参数必须是(或最好是)输入迭代器,最后一个参数必须是(或最好是)输出迭代器。
这里使用ostream_iterator模板(适配器),将其他接口转换为STL使用的接口。代码中现在out_iter迭代器是一个接口,让你使用cout来显示信息。
/*也可以不创建命名的迭代器,而直接构建一个匿名的迭代器。即可以这样使用适配器*/
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout," "));
再举个例子参考
- Insert Iterator:将容器绑定到back_insert_iterator、front_insert_iterator、insert_iterator。它们都是一个类,对它们的赋值操作将转换为对绑定容器的插入操作。为了操作方便,向用户提供的是一个函数,函数中才创建上述类。
以back_inserter()函数为例:
template <class Container>
inline back_insert_iterator<Container> back_inserter(Container& x) {
return back_insert_iterator<Container>(x); // 以容器为参数,创建迭代器适配器
}
注意,一般迭代器适配器不会以迭代器作为参数,这里通过传入一个容器创建一个迭代器适配器。
下面是back_insert_iterator类的定义:
template <class Container>
class back_insert_iterator {
protected:
Container* container; // 注意,这里是一个指向容器的指针
public:
typedef output_iterator_tag iterator_category; // 输出迭代器,只支持自增
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
explicit back_insert_iterator(Container& x) : container(&x) {} // 与容器相绑定
back_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_back(value);
return *this;
}
back_insert_iterator<Container>& operator*() { return *this; }
back_insert_iterator<Container>& operator++() { return *this; }
back_insert_iterator<Container>& operator++(int) { return *this; }
};
可以看到,迭代器适配器提供了自增和接引用的接口,但是实际的功能被关闭了。上述代码的关键在于,对迭代器适配器的赋值变为了对容器的插入操作。
下面是作者写的一个类似于上面的迭代器适配器:
#include <iostream>
#include <vector>
using namespace std;
template <class Container>
class my_back_insert_iterator {
protected:
Container* container;
public:
explicit my_back_insert_iterator(Container& x) : container(&x) {}
my_back_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_back(value);
return *this;
}
};
int main()
{
vector<int> vec;
my_back_insert_iterator< vector<int> > back_insert(vec);
back_insert = 1;
back_insert = 2;
back_insert = 3;
back_insert = 4;
vector<int>::iterator iter = vec.begin();
for ( ; iter != vec.end(); ++iter)
cout << *iter << endl;
return 0;
}
容器种类
STL具有容器概念和容器类型。
概念是具有名称(如容器、序列容器、关联容器)的通用类别。(大类)
容器类型是可用于创建具体容器对象的模板(deque、list、queue、stack、vector、map等)。(小类)